Firebase数据库快速入门处理计数的方式是否安全?

Zig*_*rve 16 android firebase firebase-realtime-database

我想为文章喜欢创建一个增量字段.

我指的是以下链接:https://firebase.google.com/docs/database/android/save-data#save_data_as_transactions

在示例中,有增量字段的代码:

if (p.stars.containsKey(getUid())) {
    // Unstar the post and remove self from stars
    p.starCount = p.starCount - 1;
    p.stars.remove(getUid());
} else {
    // Star the post and add self to stars
    p.starCount = p.starCount + 1;
    p.stars.put(getUid(), true);
}
Run Code Online (Sandbox Code Playgroud)

但是,我怎么能确定用户是否已经喜欢/不喜欢这篇文章?

在这个例子中,用户(黑客)也可以像这样清除整个星图,它会保存:

p.stars = new HashMap<>();
Run Code Online (Sandbox Code Playgroud)

它会破坏已经喜欢它的其他用户的逻辑.

我甚至认为你不能为此制定规则,尤其是"减少计数"行动.

任何帮助,建议?

Fra*_*len 26

安全规则可以做一些事情:

  • 确保用户只能添加/删除自己uidstars节点

    "stars": {
      "$uid": {
        ".write": "$uid == auth.uid"
      }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 确保用户只能更改starCount他们自己添加uidstars节点或从那里删除它的时间

  • 确保用户只能增加/减少starCount1

即使有这些,拥有一个安全规则确保它starCount等于stars节点中的uid数量确实仍然很棘手.我鼓励你尝试一下,然后分享你的结果.

我看到大多数开发人员处理这个问题的方式是:

  • 在客户端上开始计数(如果stars节点的大小不是太大,这是合理的).
  • 在聚合了starsinto 的服务器上运行受信任的进程starCount.它可以使用child_added/child_removed事件来递增/递减.

更新:用工作示例

我写了一个投票系统的实例.数据结构是:

votes: {
  uid1: true,
  uid2: true,
},
voteCount: 2
Run Code Online (Sandbox Code Playgroud)

当用户投票时,该应用会发送多位置更新:

{
  "/votes/uid3": true,
  "voteCount": 3
}
Run Code Online (Sandbox Code Playgroud)

然后删除他们的投票:

{
  "/votes/uid3": null,
  "voteCount": 2
}
Run Code Online (Sandbox Code Playgroud)

这意味着应用程序需要显式读取当前值voteCount,其中:

function vote(auth) {
  ref.child('voteCount').once('value', function(voteCount) {
    var updates = {};
    updates['votes/'+auth.uid] = true;
    updates.voteCount = voteCount.val() + 1;
    ref.update(updates);
  });  
}
Run Code Online (Sandbox Code Playgroud)

它本质上是一个多地点交易,但后来内置了应用程序代码和安全规则,而不是Firebase SDK和服务器本身.

安全规则做了一些事情:

  1. 确保voteCount只能上升或下降1
  2. 确保用户只能添加/删除自己的投票
  3. 确保计票增加伴随投票
  4. 确保计数减少伴随着"未投票"
  5. 确保投票伴随计数增加

请注意,规则不会:

  • 确保"未投票"伴随计数减少(可以通过.write规则完成)
  • 重试失败的投票/未投票(处理并发投票/取消投票)

规则:

"votes": {
    "$uid": {
      ".write": "auth.uid == $uid",
      ".validate": "(!data.exists() && newData.val() == true &&
                      newData.parent().parent().child('voteCount').val() == data.parent().parent().child('voteCount').val() + 1
                    )"
    }
},
"voteCount": {
    ".validate": "(newData.val() == data.val() + 1 && 
                   newData.parent().child('votes').child(auth.uid).val() == true && 
                   !data.parent().child('votes').child(auth.uid).exists()
                  ) || 
                  (newData.val() == data.val() - 1 && 
                   !newData.parent().child('votes').child(auth.uid).exists() && 
                   data.parent().child('votes').child(auth.uid).val() == true
                  )",
    ".write": "auth != null"
}
Run Code Online (Sandbox Code Playgroud)

jsbin用一些代码来测试这个:http://jsbin.com/yaxexe/edit?js,console