Firestore安全规则:如何用子集合字段验证读取请求
Firestore安全规则修正方案
原规则的核心问题
- 读取请求无
request.data:request.data仅在写入操作(创建/更新)中存在,读取请求里根本拿不到这个值,这是规则失效的主要原因。 - 路径匹配范围错误:你把全局集合的匹配规则嵌套在
/users/{userId}下,导致规则只对users集合下的子集合生效,无法覆盖其他顶级集合。 - 权限检查逻辑不匹配数据结构:如果
shops子集合的文档ID不是shopId(而是随机生成的),exists(/.../shops/$(request.data.shopId))会永远返回false,因为找不到对应ID的文档。
修正后的规则(推荐方案)
前提:将users/{userId}/shops子集合的文档ID设置为对应的shopId(通过文档路径直接验证权限,性能最优)。
客户端需通过自定义身份验证声明传递shopId(这是读取请求中传递可信参数的安全方式,客户端无法篡改)。
service cloud.firestore { match /databases/{database}/documents { // 复用权限检查逻辑的自定义函数 function userHasAccessToShop(shopId) { return request.auth != null && exists(/databases/$(database)/documents/users/$(request.auth.uid)/shops/$(shopId)); } // 允许用户读取自己的用户文档(可根据需求调整) match /users/{userId} { allow read: if request.auth != null && request.auth.uid == userId; } // 匹配所有顶级集合及子文档 match /{collection}/{document=**} { allow read: if userHasAccessToShop(request.auth.token.shopId) && resource.data.shopId == request.auth.token.shopId; } } }
关键调整说明
- 函数复用逻辑:把权限检查抽成函数,避免重复代码,更易维护。
- 全局集合匹配:将
/{collection}/{document=**}放在外层,确保覆盖所有集合和子文档。 - 安全传递shopId:通过
request.auth.token.shopId获取请求携带的shopId,由后端生成自定义token时注入,客户端无法篡改。 - 双重验证:先确认用户有权访问该shopId,再验证要读取的文档的
shopId与请求一致。
若shops子集合文档ID不是shopId的处理方式
如果无法修改文档ID,可在用户文档中维护allowedShopIds数组(存储用户有权访问的所有shopId),规则调整如下:
service cloud.firestore { match /databases/{database}/documents { function userHasAccessToShop(shopId) { return request.auth != null && shopId in get(/databases/$(database)/documents/users/$(request.auth.uid)).data.allowedShopIds; } match /users/{userId} { allow read: if request.auth != null && request.auth.uid == userId; } match /{collection}/{document=**} { allow read: if userHasAccessToShop(request.auth.token.shopId) && resource.data.shopId == request.auth.token.shopId; } } }
这种方式需要在用户权限变更时同步更新allowedShopIds数组。
内容的提问来源于stack exchange,提问作者Herman Mwageni
相关产品推荐
相关产品推荐

