如何在增加Firebase投票数量的同时防止重复投票? [英] How do I prevent duplicate voting while incrementing the vote count on Firebase?

查看:165
本文介绍了如何在增加Firebase投票数量的同时防止重复投票?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在Firebase中建立一个实时轮询。每个投票都存储在一个列表字段中。为了防止不得不把每一个投票都拖到客户端来计算它们,我将每个选项的计数器缓存在计数器字段中。

  poll1 
counts
choice1:5
choice2:2
choice3:10
choice4:252
选项
uid1:choice1
uid6:choice3
uid25:choice2
uid31:choice1

m当前用以下事务更新计数器:

$ p $ var $ fireCountPush = new Firebase(self.fireCountUrl + node + id);
fireCountPush.transaction(function(current_value){
return current_value + 1;
},function(error,committed,snapshot){
if(committed){
var fireVote = new Firebase(self.fireVotesUrl);
fireVote.child(self.user.uid).set(id);
}
});

但是,我想以原子方式将用户添加到选民列表中,理想情况下在同一个事务中。不幸的是,现在我已经有了这个方法,在事务提交成功之后,我不得不添加用户。这是一个巨大的安全问题,因为它可以很容易地通过编辑脚本在浏览器中禁用。



是否有任何方法来更新计数器和 em>添加一个用户到选民列表中,而无需在交易中下载整个对象?

最简单的答案在这里是有一个中立的一方(服务器脚本)监视选民名单和增加计数器。那么只需要确保用户通过他们的uid添加他们自己,只能这样做一次。

我相信也有一些很好的方法来完成这个完全安全规则。不幸的是,我不是那么出色,但是如果你真的想要一个客户端解决方案的痛苦的话,这里有一个强悍的方法可以提高你的工作效率。






  • 强制用户先写审计记录

  • 选民列表第二

  • 允许他们更新计数器,当这两个记录存在和匹配的投票号码


$
$ b

  / audit /< $ x> /< $ user_id> 
/选民/ $ user_id /< $ x>
/ total /< $ x>

如果用户已经投票(选项/ $ user_id存在),我们阻止用户修改审计/如果审计记录已经存在(某人已经声明了这个计数),或者如果投票没有增加一个:

  audit:{
$ x:{
.write:newData.exists()&&!data.exists(),//不删除,不覆盖
.validate:!root.child('votes /'+ auth.uid).exists()&& $ x === root.child('total')+ 1$ b $您可以更新审计

$ c>在一个事务中,本质上试图声明每一个增量,直到成功并取消事务(通过返回未定义的)任何时候记录被添加不为空(有人已经声称)。为了防止任何有趣的业务,我们存储了选民名单,这迫使每个选民只写入审计/一次。我只能写信给选民,如果我从来没有投票过,只有一个审计记录已经创建了我的唯一投票号码:

 .write:newData.exists()&&!data.exists(),//不删除,无替换
.validate:newData.isNumber()& root.child('audit /'+ newData.val()).val()=== $ user_id
}
}

最后但并非最不重要的是, ID。它必须匹配我的投票号码,它可能只会增加。这可以防止一个用户创建审计记录和选民记录的竞争状况,但是在我完成我的三个步骤之前,其他人已经增加了总额。 code>total:{
.write:newData.exists(),//不删除
.validate:newData.isNumber()&& newData.val()=== root.child('audit /'+ auth.uid).val()&& newData.val()> data.val()
}

更新总数(如添加初始审计记录)将在事务中完成。如果总数的当前值大于我分配的投票数,那么我只是用 undefined 来取消交易,因为其他人已经投了我的票,把它更新了。不是问题。


I'm building a real-time poll in Firebase. Each vote is stored in a list field. To prevent having to pull every vote down to the client in order to count them, I cache the tallies for each choice in counter fields.

poll1
    counts
        choice1: 5
        choice2: 2
        choice3: 10
        choice4: 252
    voters
        uid1 : choice1
        uid6 : choice3
        uid25: choice2
        uid31: choice1

I'm currently updating the counter with the following transaction:

var fireCountPush = new Firebase(self.fireCountUrl+node+id);
fireCountPush.transaction(function(current_value) {
    return current_value + 1;
}, function(error, committed, snapshot) {
    if(committed) {
        var fireVote = new Firebase(self.fireVotesUrl);
        fireVote.child(self.user.uid).set(id);
    }
});

However, I want to atomically add the user to the voters list, ideally in the same transaction. Unfortunately, the way I have it now, I'm having to add the user after the transaction commits successfully. This is a huge security issue since it can be easily disabled in the browser by editing the script.

Is there any way to both update the counter and add a user to the list of voters without having to download the entire object in the transaction?

解决方案

The simplest answer here is to have a neutral party (a server script) monitor the list of voters and increment the counter. Then one only need make sure users add themselves by their uid and can only do so once.

I'm sure there are also some brilliant ways to do this entirely with security rules. I'm, unfortunately, not that brilliant, but here's a brute force approach you can improve on if you really want the pain of a client-only solution.

The plan:

  • force the user to write an audit record first
  • force them to add their name to the list of voters second
  • allow them to update the counter when both of these records exist and match the vote number

The schema:

/audit/<$x>/<$user_id>
/voters/$user_id/<$x>
/total/<$x>

We prevent user from modifying audit/ if they have already voted (voters/$user_id exists), or if the audit record already exists (someone has already claimed that count), or if the vote is not incremented by exactly one:

"audit": {
  "$x": {
     ".write": "newData.exists() && !data.exists()", // no delete, no overwrite
     ".validate": "!root.child('voters/'+auth.uid).exists() && $x === root.child('total')+1"
  }
}

You would update audit in a transaction, essentially trying to "claim" each increment until successful and cancelling the transaction (by returning undefined) any time the record to be added is not null (someone has already claimed it). This gives you your unique vote number.

To prevent any funny business, we store a list of voters, which forces each voter to only write into audit/ once. I can only write to voters if I've never voted before and only if an audit record has already been created with my unique vote number:

"voters": {
  "$user_id": {
     ".write": "newData.exists() && !data.exists()", // no delete, no replace
     ".validate": "newData.isNumber() && root.child('audit/'+newData.val()).val() === $user_id"
  }
}

Last, but not least, we update the counter to match our claimed vote id. It must match my vote number and it may only increase. This prevents a race condition where one user creates an audit record and voters record, but someone else has already increased the total before I finished my three steps.

"total": {
  ".write": "newData.exists()", // no delete
  ".validate": "newData.isNumber() && newData.val() === root.child('audit/'+auth.uid).val() && newData.val() > data.val()"
}

Updating the total, like adding the initial audit record, would be done in a transaction. If the current value of the total is greater than my assigned vote number, then I just cancel the transaction with undefined because someone else has already voted after me and updated it higher. Not a problem.

这篇关于如何在增加Firebase投票数量的同时防止重复投票?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆