Firebase数据库-将限制值更新为每个用户仅一次

时间:2018-09-23 19:32:45

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

我正在Android应用中使用Firebase Realtime数据库, 该应用包含文章,我想通过所有应用用户将“观看次数”添加到文章中。

数据库包含一个子项(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

2 个答案:

答案 0 :(得分: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中读取内容,然后再尝试对当前用户进行计数。但是无论哪种方式:使用这些规则,每个用户只能计数一次。


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

Firebase Realtime Database rules simulator

这是我测试过的JSON:

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

答案 1 :(得分:0)

这是在其他任何人遇到相同问题的情况下起作用的安全规则:

{"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"}},

     },