【问题标题】:Firebase Database -Restrict value update to only once per userFirebase 数据库 - 将每个用户的值更新限制为一次
【发布时间】:2018-10-05 06:28:07
【问题描述】:

我在 Android 应用中使用 Firebase 实时数据库, 该应用程序包含文章,我想通过所有应用程序用户添加文章的查看次数。

数据库包含一个子 (ViewsCount) 它的值(是一个数字) 每次新用户查看文章时,我都需要更新该值(增加 1)(类似于 YouTube 视频查看的概念)

用户必须通过匿名身份验证才能更新值,但不能多次这样做。

我将逻辑集成到应用程序中来做到这一点并且效果很好。 但我担心的是,如果有人试图在我的应用程序之外(使用他们自己的代码)连接到数据库并不断增加值。

我目前的安全规则是:

"ViewsCount"
{
  ".write": "newData.exists() && auth!==null",
  ".validate" "newData.val()===data.val()+1"
}
"users": { 
  "$user_id": { 
    ".write": "auth.uid===$user_id", 
    ".read": "auth.uid===$user_id ",
  }
}

有没有办法通过安全规则将该值的更新限制为每个用户一次? 错误:

更新: screenshot from Simulator Database structure

【问题讨论】:

    标签: android firebase-realtime-database firebase-security


    【解决方案1】:

    当然可以,但您必须做一些额外的工作。首先是在数据库中跟踪谁已经查看了每篇文章。例如,保留他们的 UID 列表,如下所示:

    viewedBy: {
      uidOfKarim: true,
      uidOfPuf: true
    }
    

    现在,当有人查看文章时,他们会将其 UID 写入 viewedBy,同时增加该文章的查看次数。在代码中可以很简单:

    DatabaseReference countRef = FirebaseDatabase.getInstance().getReference("ViewsCount");
    countRef.addListenerForSingleValueEvent(new ValueEventListener() {
      @Override
      public void onDataChange(DataSnapshot dataSnapshot) {
        String uid = FirebaseAuthentication.getCurrentUser().getUid();
        Long newCount = dataSnapshot.getValue(Long.class) + 1;
        Map<String, Object> updates = new HashMap<String, Object>();
        updates.put("viewedBy/"+uid, true);
        updates.put("ViewsCount", newCount);
      }
    
      @Override
      public void onCancelled(DatabaseError databaseError) {
        throw databaseError.toException();
      }
    }
    

    所以这段代码将用户的UID新的视图计数发送到数据库。现在我们将安全性更新为仅在 UID 尚未存储且计数增加 1 时才接受写入操作:

    "viewedBy": { 
      "$user_id": { 
        ".validate": "
          auth.uid===$user_id && !data.exists() && newData.exists() &&
          newData.parent().parent().child('ViewsCount').val() === data.parent().parent().child('ViewsCount').val()+1
        "
      }
    },
    

    语法有点长,因为它同时检查ViewsCountViewedBy 数据。只有当它增加 1 并且来自尚未计数的用户时,写入才会成功。如果它们之前被计算过,或者不加 1,则写入被拒绝。

    这里有一个极端情况:如果多个用户几乎同时查看,他们的一个写入可能会被拒绝,因为他们实际上并没有增加值:

    user1       user2                   database
      |           |                        |
    read_count ------------------->        |
      |           |                        |
      |        read_count -------->        |
      |           |                        |
      |        <------------------- 2      |
      |           |                        |
      |           |   <------------ 2      |
      |           |                        |
    set_count 3 ------------------->       |
      |           |                        |
      |        set_count -------->         |
      |           |                        |
      |        <------------------- ok     |
      |           |                        |
      |           |   <------------ ACCESS |
      |           |                 DENIED |
      |           |                        |
    

    在这种情况下,您应该重试更新操作,读取新值并据此确定正确的计数。这实际上是 Firebase 事务在幕后的工作方式。或者,您可以先阅读viewedBy 以查看当前用户是否已被计算在内,然后再尝试计算它们。但无论哪种方式:根据这些规则,每个用户只能算一次。


    更新:这是我在模拟器中测试这些规则的方式:

    这是我测试的 JSON:

    "52469568": {
      "ViewsCount": 10,
    }
    

    【讨论】:

    • 谢谢,但这里的问题和我担心的问题是,如果其他人有恶意编写不同的代码,他在不将他的 UID 添加到节点 "viewdBy ”。他只需要反编译应用程序,提取json文件,然后用相同的包名编写自己的代码。
    • 我想我要问的是是否可以在 ViewsCount 增加后强制将 Viewer UID 添加到 viewdBy 节点。并通过安全规则这样做,因为这些规则可以访问并且只能由我更改。
    • 以上规则使得如果不将您的 UID 写入viewedBy 就无法递增,每个用户根据相同的规则只能执行一次。试一试,如果它不起作用,请告诉我。
    • 谢谢你。
    • ".value":返回错误(保存规则时出错 - 第 23 行:预期为 '{'。)您的意思是“.validate”吗?
    【解决方案2】:

    这是在其他人遇到相同问题时有效的安全规则:

    {"rules": {
    "views": { 
      ".write": "auth != null",
    ".validate": "newData.hasChildren(['ViewsCount', 'viewedBy'])",
    
    "ViewsCount": {
    ".read": "auth != null",
    ".validate":"data.parent().child('viewedBy').child(auth.uid).exists()== false"
    },
    
    "viewedBy": { 
    "$user_id": { 
    ".validate": " auth.uid===$user_id && !data.exists() && newData.exists() &&
      newData.parent().parent().child('ViewsCount').val() === 
    data.parent().parent().child('ViewsCount').val()+1
    && newData.val()===true"}},
    
         },
    

    【讨论】:

      猜你喜欢
      • 2021-08-12
      • 1970-01-01
      • 1970-01-01
      • 2017-12-29
      • 1970-01-01
      • 1970-01-01
      • 2018-06-10
      • 2021-09-26
      • 2014-05-01
      相关资源
      最近更新 更多