基于Firebase的投票应用安全规则设计:解决级联访问控制难题
嘿,我来帮你搞定这个Firebase投票应用的权限设计和数据结构问题,刚好能解决你提到的两个核心需求——只有答题者看结果、只有好友看用户答题状态,还能解决你之前规则没隐藏答题者身份的问题。
推荐的数据结构
我会分成三个核心节点(以Firebase Realtime Database为例,Firestore逻辑类似,仅路径写法不同):
1. polls/{pollId}
存储投票基础信息,结果统计由Cloud Functions自动更新,避免前端篡改:
{ "question": "你喜欢咖啡吗?", "options": ["yes", "no"], "totalYes": 0, "totalNo": 0 }
2. userResponses/{userId}/{pollId}
存储单个用户的作答记录,天然隔离用户答题信息:
{ "answer": "yes", "timestamp": 1620000000000 }
3. friendships/{userId}/friends/{friendId}
用键值对存储好友关系,方便权限判断:
{ "friend123": true, "friend456": true }
这里friendId是好友的用户ID,值设为true表示已建立好友关系。
对应的安全规则
直接实现你的权限需求,逻辑清晰且符合Firebase规则特性:
1. 投票结果访问控制(仅答题者可看)
验证当前用户是否已参与该投票,只有参与者能读取结果:
{ "rules": { "polls": { "$pollId": { ".read": "exists(/database/$(database)/userResponses/$(request.auth.uid)/$(pollId))", ".write": "false" // 假设投票由管理员创建,普通用户不可修改投票内容 } } } }
2. 用户答题状态访问控制(仅好友可看)
分两种场景开放读取权限:用户自己查看,或用户的好友查看;同时限制只能提交一次答题:
{ "rules": { "userResponses": { "$userId": { ".read": "request.auth.uid == $userId || exists(/database/$(database)/friendships/$(request.auth.uid)/friends/$(userId))", "$pollId": { ".write": "request.auth.uid == $userId && !exists(/database/$(database)/userResponses/$(request.auth.uid)/$(pollId))" } } } } }
3. 好友关系权限控制
保证用户只能管理自己的好友列表:
{ "rules": { "friendships": { "$userId": { ".read": "request.auth.uid == $userId", "friends": { "$friendId": { ".write": "request.auth.uid == $userId" } } } } } }
解决你提到的核心问题
- 隐藏答题者身份:答题记录存在
userResponses节点下,而非投票节点中。非好友、非答题者没有权限读取其他用户的userResponses数据,自然看不到谁参与了投票,完美解决你之前的身份泄露问题。 - 级联限制结果访问:通过
exists(/userResponses/$(request.auth.uid)/$(pollId))直接验证用户是否参与投票,无需复杂级联查询,Firebase规则会实时完成检查,只有答题者能获取polls中的结果统计。
额外优化:自动更新投票结果
用Cloud Functions监听答题记录的写入事件,自动更新投票结果,避免前端篡改:
// Cloud Functions Node.js示例 exports.updatePollResults = functions.database.ref('/userResponses/{userId}/{pollId}') .onCreate((snapshot, context) => { const answer = snapshot.val().answer; const pollId = context.params.pollId; const updateObj = answer === 'yes' ? { totalYes: admin.database.ServerValue.increment(1) } : { totalNo: admin.database.ServerValue.increment(1) }; return admin.database().ref(`/polls/${pollId}`).update(updateObj); });
内容的提问来源于stack exchange,提问作者Jack Guo
相关产品推荐
相关产品推荐

