You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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"
          }
        }
      }
    }
  }
}

解决你提到的核心问题

  1. 隐藏答题者身份:答题记录存在userResponses节点下,而非投票节点中。非好友、非答题者没有权限读取其他用户的userResponses数据,自然看不到谁参与了投票,完美解决你之前的身份泄露问题。
  2. 级联限制结果访问:通过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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 07:49:59