Firebase实时数据库分片规则

时间:2019-10-19 04:14:38

标签: firebase firebase-realtime-database

更新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.jsonmain定位两个分片以使用此规则文件;我在部署时进行了验证)

{
 "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 ..."指示第四个请求。因此,似乎客户端完全无法对分片进行身份验证。

模拟器输出: simulator shows success with the same rules

建议?我确定我做错了什么。

顺便说一句,用户文档无处不在...所有这些实际上都有效吗?

"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

2 个答案:

答案 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支持人员回答了我对规则提出的附带问题:

  • A)RTDB使用auth,而Firestore使用request.auth
  • B)在检查auth!=null之前省略auth.uid!=null预检查 在这种情况下是可以的,因为对象上的解除引用错误会拒绝 写。
  • C)确认已将==转换为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"
      }
    }
  }
}