【问题标题】:Firebase Object Ownership with Event ObservationFirebase 对象所有权与事件观察
【发布时间】:2017-01-03 04:34:00
【问题描述】:

我在我的 iOS 应用中使用 Firebase。我希望我的每个对象都有一个 creatorId 属性,其值是经过身份验证的用户 ID(authData.uid 带有 Firebase 身份验证对象)。我正在使用自定义令牌生成器进行身份验证,但问题也可以通过匿名登录来重现。

我希望用户只能读取(和写入,但现在让我们专注于阅读,因为那是我遇到问题的地方)他们创建的对象。换句话说,查询用户的认证用户 ID 将匹配他们正在获取的对象的creatorId

当我制定查询和规则来实现这一点时,我遇到了权限问题。

Here 是 Firebase 的规则和安全文档。

这是我的 Firebase 仪表板对于 Task 对象的外观:

+ firebase-base
    + tasks
        + {task_id}
             + creatorId: 
             + title: 

其中task_id 是 Firebase 在插入时生成的唯一标识符。

我的规则是这样的(同样,让我们​​暂时忽略编写规则):

{
   "rules": {
       "tasks": {
         "$task_id": {
           ".read": "auth.uid === data.child('creatorId').val()"          
         }
       }
   }
}

读取特定任务可以正常工作,但我希望能够使用observeEventType 和相关函数进行查询“获取我创建的所有任务”。这对我不起作用。我收到“权限被拒绝”错误。

这是我在 Swift 中的观察方式:

let reference = Firebase(url: "https://{My-Firebase-Base-Reference}/tasks")
reference.observeEventType(.ChildChanged, 
     withBlock: { (snapshot: FDataSnapshot!) -> Void in

                       // Success

             }) { (error: NSError!) in

                       // Error: I get Permissions Denied here.

              }

根据@Ymmanuel 的建议,我还尝试在查询中更加具体,如下所示:

let reference = Firebase(url: "https://{My-Firebase-Base-Reference}/tasks")
reference.queryOrderedByChild("creatorId").queryEqualTo({USER_UID}).observeEventType(.ChildChanged, 
     withBlock: { (snapshot: FDataSnapshot!) -> Void in

                       // Success

             }) { (error: NSError!) in

                       // Error: I get Permissions Denied here.

              }

这些块都不起作用,我总是收到“权限被拒绝”错误。我做错了什么?

【问题讨论】:

  • 我有一个问题?使用您作为“用户 A”的规则,您可以为“用户 B”创建任务,只要您在 creatorId 中设置“用户 B”uid 对吗?
  • 你的输出是什么?您的孩子添加的事件完全被解雇了吗?或不?有时是成功有时你会被拒绝??
  • 我发表了一些部分想法,但问题确实不清楚。你到底想做什么?如果通过 .ChildChanged 观察到现有节点,则如果该节点发生更改,则会向您的应用发送事件。如果只应通知用户某些节点的更改,您只需将观察者附加到这些节点。这种能力不需要任何规则。如果您想禁止读取将使用规则完成的节点。你能澄清你的后事吗?也许是一个用例?
  • @Jay 很抱歉造成混乱。我编辑了这个问题,希望能提高清晰度。让我知道这是否有帮助。
  • 规则不能用于过滤数据。请参阅stackoverflow.com/a/14298525/209103 以获得最佳解释。正如 Jay 所说:ChildChanged 只会在现有孩子发生变化时触发,您更有可能在寻找 ChildAdded。但除非你解决“规则不是过滤器”的要求,否则什么都行不通。

标签: ios firebase


【解决方案1】:

您缺少的是假设安全规则是查询,而事实并非如此。

检查 链接中的Rules are Not Filters 部分。

安全规则仅验证您是否可以读取或写入 Firebase 数据库的特定路径。

如果您只想接收特定用户的更改,您应该使用 firebase 查询。

例如,如果你想获取特定用户的所有任务,你应该这样做:

let reference = Firebase(url: "https://{My-Firebase-Base-Reference}/tasks")
reference.queryOrderedByChild("creatorId").queryEqualTo(YOUR_CURRENT_USER_UID).observeEventType(.ChildChanged, 
     withBlock: { (snapshot: FDataSnapshot!) -> Void in
          // Success
     }) { (error: NSError!) in
          // Error: Get Permissions Denied Here.
}

这样您可以获得所有仅与您的用户相关的事件,并通过应用安全规则来保护信息。

此外,如果您只想允许创建者编写自己的任务,您还应该考虑创建任务并编写如下内容的情况:

  "tasks": {
     //You need to include the $task_id otherwise the rule will seek the creatorId child inside task and not inside your auto-generated task
     "$task_id": {
       ".read": "auth.uid === data.child('creatorId').val()",
       //this is to validate that if you are creating a task it must have your own uid in the creatorId field and that you can only edit tasks that are yours...
       ".write":"(newData.child('creatorId').val() === auth.uid && !data.exists()) || (data.child('creatorId').val() === auth.uid && data.exists())",
       //you should add the index on to tell firebase this is  a query index so your queries can be efficient even with larger amounts of children
       ".indexOn":"creatorId",        
     }
   }

(检查语法,但这是一般的想法)

【讨论】:

  • 谢谢,您的 cmets 确实告知了我做错了什么。我仍然在让查询工作时遇到一些麻烦。由于在您的示例中我的参考位于tasks,而creatorId 实际上是单个任务的子任务(在taskId 下),我是否真的想以不同的方式构造该查询?在我看来,creatorId 是您的示例中我的基本参考的孩子的孩子。
  • queryOrderedByChild('creatorId') 结合 queryEqualTo(current-user-uid) 等于说在我不知道 $task_id 的所有任务中搜索,并把所有的任务带给我creatorId 等于 current-user-uid
  • 您写的所有内容都有意义,但我遇到了同样的问题,我的 observeEventType 块永远不会被调用。
  • 我不确定queryEqualToValue 是否符合您的想法。这不会在tasks 端点中搜索任何具有YOUR_CURRENT_USER_UID 值(在本例中为taskId)的对象吗?
  • 我建议您分两步执行此操作,首先删除所有将读写设置为 true 的规则,测试查询是否正常,然后添加规则,这样您就可以确定错误是否是在规则或查询本身...一旦您验证我们可以找到解决方案
【解决方案2】:

几个cmets:

如果本地持久性打开,并且您没有互联网连接并且没有本地值,则不会调用任何块。

问题中的观察代码困扰着我,这并没有错,但如果是这样的话可能会更清楚一些:

    reference.observeEventType(.ChildChanged, withBlock: { snapshot in

            print(snapshot.value)

        }, withCancelBlock: { error in

            print(error.description)

    })

编辑:

现在问题更清楚了

我希望用户只能读取他们创建的对象。

我将通过以下方式进行演示:

Firebase 结构:

posts
  post_0
    created_by: uid_0
    msg: some message
  post_1
    created_by: uid_1
    msg: another message
  post_2
    created_by: uid_2
    msg: yippee

以及允许用户仅从他们创建的帖子节点读取的规则

"rules": {
    ".read": false,
    ".write": false,
    "posts": {
      ".read": false,
      "$post_id": {
        ".read": "root.child('posts').child($post_id).child('created_by').val() == auth.uid",
        ".write": "auth != null"
      }
    }
  }

然后是要测试的代码。我们假设当前用户的 uid 是 uid_0:

let reference = self.myRootRef.childByAppendingPath("posts/post_0")

reference.observeEventType(.ChildAdded, withBlock: { snapshot in

        print(snapshot.value)

    }, withCancelBlock: { error in

        print(error.description)    
})

以上代码将允许从节点 post_0 读取,该节点是由 created_by 子节点指示创建的 post uid_0。

例如,如果路径更改为 posts/posts_1(或其他任何内容),则读取被拒绝。

这个答案我没有直接回答这个问题,就好像您期望规则“阻止”或“过滤”结果数据一样,这不是规则的用途(根据其他答案)。

因此,您可能想走一条不同的道路,方法是构建一个查询,以根据 created_by = auth.uid 仅提取您想要使用的帖子,就像这样

let reference = self.myRootRef.childByAppendingPath("posts")

reference.queryOrderedByChild("created_by").queryEqualToValue("uid_1")
    .observeEventType(.Value, withBlock: { snapshot in

            print(snapshot.value)

        }, withCancelBlock: { error in

            print(error.description)    
    })

【讨论】:

  • 这个问题与本地持久化无关。我同意,该代码更简洁,我想明确说明示例中的类型。
  • @TimCamber 最初的问题是没有调用任何块,所以我想提供一个可能的原因。
  • 这很有帮助,但我不确定它是否能解决我的问题。我想观察posts,但只看到created_by 与查询客户端的uid 匹配的事件。在这种情况下,我们可以读取用户已经创建的特定对象的节点,但如果在不同的客户端设备(相同的created_by)上创建新的post 对象怎么办?我们如何观察这种变化?这是不可能的吗?
  • 我理解“规则不是过滤器”。不过,我希望规则和查询能够协同工作。我想根据查询获取数据(“仅获取与我的created_by 匹配的对象”),但我还想在 Firebase 上强制执行类似的访问规则。
  • 我完全按照你在回答结尾处的建议做了。但是,只要有任何created_by 规则到位,observeEventType 调用总是会失败并显示“权限被拒绝”,如我的问题中所述。
猜你喜欢
  • 2016-12-11
  • 2012-12-14
  • 1970-01-01
  • 2017-09-07
  • 2019-03-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-29
相关资源
最近更新 更多