更新2019-11-03:添加了live minimal reproduction of the error。在Chrome中加载链接后,点击ctrl + shift + i并选择控制台以查看输出。我已经尽力确保它的功能与我原始项目的代码完全相同。我们看看是否是这样,是吗?分片的规则文件与下面的原始文章相同。 source is available on GitHub。
<!DOCTYPE html>
<html>
<body>
<script src="https://www.gstatic.com/firebasejs/7.2.3/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/7.2.3/firebase-auth.js"></script>
<script src="https://www.gstatic.com/firebasejs/7.2.3/firebase-database.js"></script>
<script>
const config={
apiKey: "AIzaSyDLMc0GUf5n2nQa3aqpELQu7lziprQOGs8",
authDomain: "shardautherror.firebaseapp.com",
databaseURL: "https://shardautherror.firebaseio.com",
projectId: "shardautherror",
storageBucket: "shardautherror.appspot.com",
messagingSenderId: "841096336504",
appId: "1:841096336504:web:9899961c8250caa552498d"
};
const shard="https://shardautherror-1e9ed.firebaseio.com/";
async function init(){
try{
firebase.database.enableLogging(true);
const defaultApp=firebase.initializeApp(config);
const auth=defaultApp.auth();
const s="alice@example.com";
await auth.signInWithEmailAndPassword(s,s);
const uid= auth.currentUser.uid;
const shardApp=firebase.initializeApp({databaseURL:shard},'dbAppShard');
const db=firebase.database(shardApp);
const ref= db.ref("/chat/"+uid+"/fail/"+uid);
const time= firebase.database.ServerValue.TIMESTAMP;
ref.set({time});
} catch(e) {
console.error("init failed",e);
}
}
init();
</script>
</body>
</html>
原始帖子:
这些规则在模拟器中有效,但在我的实际Web应用程序中无效。模拟器的路径和有效负载与下面的数据库日志输出中所示的相同。
database.rules.json
(main
定位两个分片以使用此规则文件;我在部署时进行了验证)
{
"rules":{
"chat":{
"$ownerId":{
"fail":{
"$pId":{
".write": "$pId== auth.uid&& $ownerId== auth.uid",
"time":{".validate": "newData.val()== now"},
"$other":{".validate": "newData.isString()&& newData.val().length>= 28"}
}
}
}
}
}
}
setbase命令的Firebase日志输出失败。它只写一个称为时间的值。这是我第一次尝试使用rtdb。我已经设置了分片。它会在尝试访问实时数据库之前从firestore中获取分片名称,但是由于下面将要概述的原因,它似乎并不存在竞争条件(尽管有日志记录输出)。
index.esm.js:81 [2019-10-19T03:02:53.281Z] @firebase/database: 0: set
{"path":"/chat/rpNIK41hNpWkYY2KqndkwCzPJuF3/fail/rpNIK41hNpWkYY2KqndkwCzPJuF3",
"value":{"time":{".sv":"timestamp"}},"priority":null}
22:02:53.285 index.esm.js:81 [2019-10-19T03:02:53.285Z] @firebase/database:
p:0: Buffering put: /chat/rpNIK41hNpWkYY2KqndkwCzPJuF3/fail/rpNIK41hNpWkYY2KqndkwCzPJuF3
22:02:53.293 index.esm.js:81 [2019-10-19T03:02:53.293Z] @firebase/database:
p:0: Making a connection attempt
22:02:53.294 index.esm.js:81 [2019-10-19T03:02:53.294Z] @firebase/database:
getToken() completed. Creating connection.
22:02:53.295 index.esm.js:81 [2019-10-19T03:02:53.295Z] @firebase/database:
c:0:0: Connection created
22:02:53.296 index.esm.js:81 [2019-10-19T03:02:53.296Z] @firebase/database:
p:0: Auth token refreshed
22:02:53.298 index.esm.js:81 [2019-10-19T03:02:53.298Z] @firebase/database:
c:0:0:0 Websocket connecting to wss://quickstart-1551998385825-7f7a6.firebaseio.com/.ws?v=5
22:02:53.534 index.esm.js:81 [2019-10-19T03:02:53.534Z] @firebase/database:
c:0:0:0 Websocket connected.
22:02:53.539 index.esm.js:81 [2019-10-19T03:02:53.539Z] @firebase/database:
c:0:0: Realtime connection established.
22:02:53.539 index.esm.js:81 [2019-10-19T03:02:53.539Z] @firebase/database:
p:0: connection ready
22:02:53.542 index.esm.js:81 [2019-10-19T03:02:53.541Z] @firebase/database:
p:0: reportStats {"c":{"sdk.js.7-0-0":1}}
22:02:53.542 index.esm.js:81 [2019-10-19T03:02:53.542Z] @firebase/database:
p:0: {"r":1,"a":"s","b":{"c":{"sdk.js.7-0-0":1}}}
22:02:53.546 index.esm.js:81 [2019-10-19T03:02:53.546Z] @firebase/database:
p:0: {"r":2,"a":"p","b":{"p":"/chat/rpNIK41hNpWkYY2KqndkwCzPJuF3/fail/rpNIK41hNpWkYY2KqndkwCzPJuF3",
"d":{"time":{".sv":"timestamp"}}}}
22:02:53.591 index.esm.js:81 [2019-10-19T03:02:53.591Z] @firebase/database:
p:0: from server: {"r":1,"b":{"s":"ok","d":""}}
22:02:53.595 index.esm.js:81 [2019-10-19T03:02:53.595Z] @firebase/database:
c:0:0: Primary connection is healthy.
22:02:53.596 index.esm.js:81 [2019-10-19T03:02:53.596Z] @firebase/database:
p:0: from server: {"r":2,"b":{"s":"permission_denied","d":"Permission denied"}}
22:02:53.597 index.esm.js:81 [2019-10-19T03:02:53.597Z] @firebase/database:
p:0: p response {"s":"permission_denied","d":"Permission denied"}
因此,在此之后,如果我将规则更新为".write": true,
,则时间戳的写入成功。在日志中它显示"r":3 ...
,所以我知道它并没有放弃连接并重新启动。然后,如果我将其更改为".write": "auth.uid != null",
或".write": "auth != null",
(因此,不检查所有权,只是检查客户端是否登录,与上面的方法不同),它将再次拒绝权限,其中"r":4 ..."
指示第四个请求。因此,似乎客户端完全无法对分片进行身份验证。
建议?我确定我做错了什么。
顺便说一句,用户文档无处不在...所有这些实际上都有效吗?
"baskets": {
".read": "auth.uid != null &&// auth.uid!= null from https://firebase.google.com/docs/database/security/securing-data
".read": "auth != null && auth.uid == $uid" // auth != null from https://firebase.google.com/docs/database/security/user-security
".write": "$user_id === auth.uid" // triple equal from https://firebase.google.com/docs/database/security/user-security
".write": "request.auth.uid == uid" // request.auth from realtime database tab of content owner access from https://firebase.google.com/docs/rules/basics
答案 0 :(得分:0)
这里有两个问题,问题的第一部分需要更多信息。
问题的第二部分
顺便说一句,用户文档无处不在...是否全部 这些实际上有效吗?
文档并不是真的到处都是。您包括的每个规则样本都来自不同的用例。
例如,“购物篮”中的.read规则适用于该特定节点“购物篮”,以确保只有经过身份验证的用户才能读取购物篮节点。并且它将允许任何经过身份验证的用户读取该节点。该行中还有一个&&,所以不确定规则的其余部分是什么。
第二条读取规则将适用于它所在的任何节点,并确保用户已通过身份验证,并且只有经过身份验证的用户才能读取该节点(即,这是他们的节点,没有其他人可以访问它)
对于写操作,documentation中包含===(等于三倍)并说
注意:: ==被视为===。如果您在安全规则中使用==,它将 规则运行时将转换为===。
最后一次写操作只是检查请求的uid是否是当前经过身份验证的用户。
答案 1 :(得分:0)
此行
const shardApp=firebase.initializeApp({databaseURL:shard},'dbAppShard');
需要更改为:
const shardApp=firebase.initializeApp({...config,databaseURL:shard},'dbAppShard');
如果客户端最近已登录 shardApp 的auth()
对象,则此方法将起作用。如果没有,则可以通过shardApp.auth().signInWithEmailAndPassword(email,pw)
和shardApp.auth().updateCurrentUser(user)
之类的函数来完成,其中user
可能来自于默认应用的auth().currentUser
对象。另外,目前仍然可以只使用默认应用程序,然后调用defaultApp.database(shardURL)
,但是在撰写本文时,我不知道此方法是否仍将保留在Firebase中。
current firebase sharding documentation仅显示客户端将databaseURL
参数仅 传递给initializeApp
,不足以使用基于身份验证的规则。与此admin Functions answer和此older-syntax multi-database sharding intro blog post不同,当前文档要求客户端构造和管理多个应用程序对象。作为Firebase的支持,我发现,非默认应用程序当前不从默认应用程序对象(也就是第一个应用程序,没有为initializeApp
创建第二个参数的情况下)中提取API密钥和其他参数。
Firebase支持人员回答了我对规则提出的附带问题:
auth
,而Firestore使用request.auth
auth!=null
之前省略auth.uid!=null
预检查
在这种情况下是可以的,因为对象上的解除引用错误会拒绝
写。==
转换为RTDB中的===
情况。请注意,A)表示此example from the documentation是错误的,因为它在RTDB中使用了request.auth
:
{
"rules": {
"some_path": {
"$uid": {
// Allow only authenticated content owners access to their data
".read": "request.auth.uid == uid"
".write": "request.auth.uid == uid"
}
}
}
}