如何通过Firestore安全规则强制多文档事务同步更新(无云函数)
Firestore安全规则:转账事务的双向原子验证(无需云函数)
场景是要同步维护accounts(含balance字段)和transfers(存储转账记录)两个集合,必须确保:
- 创建
transfers文档时,必须同时在事务中更新对应转出/转入账户的余额 - 更新
accounts文档的余额时,必须附带对应的transfers创建操作作为依据,不能单独修改账户余额
现有规则已经完成了transfers的创建验证,下面补充accounts的更新规则,实现双向约束:
rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /transfers/{transferId} { allow create: if request.auth != null && // 验证转出账户余额减少金额等于转账金额 getAfter(/databases/$(database)/documents/accounts/$(request.resource.data.fromAccountId)).data.balance - get(/databases/$(database)/documents/accounts/$(request.resource.data.fromAccountId)).data.balance === -request.resource.data.amount && // 验证转入账户余额增加金额等于转账金额 getAfter(/databases/$(database)/documents/accounts/$(request.resource.data.toAccountId)).data.balance - get(/databases/$(database)/documents/accounts/$(request.resource.data.toAccountId)).data.balance === request.resource.data.amount; } match /accounts/{accountId} { allow update: if request.auth != null && // 确保只修改balance字段,避免无关篡改 request.writeFields.size() == 1 && request.writeFields.has('balance') && ( // 情况1:当前账户是转账转出方,余额减少 ( request.resource.data.balance < resource.data.balance && existsAfter(/databases/$(database)/documents/transfers/$(request.resource.data.transferId)) && getAfter(/databases/$(database)/documents/transfers/$(request.resource.data.transferId)).data.fromAccountId == accountId && getAfter(/databases/$(database)/documents/transfers/$(request.resource.data.transferId)).data.amount == resource.data.balance - request.resource.data.balance ) || // 情况2:当前账户是转账转入方,余额增加 ( request.resource.data.balance > resource.data.balance && existsAfter(/databases/$(database)/documents/transfers/$(request.resource.data.transferId)) && getAfter(/databases/$(database)/documents/transfers/$(request.resource.data.transferId)).data.toAccountId == accountId && getAfter(/databases/$(database)/documents/transfers/$(request.resource.data.transferId)).data.amount == request.resource.data.balance - resource.data.balance ) ); } } }
规则逻辑说明:
- transfers创建规则优化:调整了余额变化的计算顺序,更直观体现转出账户余额减少值等于转账金额、转入账户余额增加值等于转账金额
- accounts更新规则核心:
- 先限制只能修改
balance字段,避免其他字段被随意改动 - 分两种余额变化场景做验证:
- 转出场景:余额减少的金额,必须等于事务中同时创建的转账记录金额,且该记录的转出账户ID就是当前账户ID
- 转入场景:余额增加的金额,必须等于事务中同时创建的转账记录金额,且该记录的转入账户ID就是当前账户ID
- 通过
existsAfter和getAfter验证事务中确实存在对应的转账记录创建操作,确保两个操作是原子绑定的
- 先限制只能修改
客户端事务要求:
执行转账时,客户端必须在同一个事务中完成三个操作:
- 创建
transfers文档,包含fromAccountId、toAccountId、amount字段 - 更新转出账户的
balance,同时在更新数据中加入transferId字段(对应刚创建的转账记录ID) - 更新转入账户的
balance,同时在更新数据中加入transferId字段(对应同一个转账记录ID)
这样就能彻底避免用户绕过事务单独修改任一集合的情况,完全通过安全规则实现约束,无需依赖云函数。
内容的提问来源于stack exchange,提问作者Menas
相关产品推荐
相关产品推荐

