Firestore子集合列表权限问题:无法限制groups子集合列表访问
问题分析与解决方案
核心问题
你的权限问题主要由路径拼写错误和集合查询的规则限制导致:
- 路径拼写不匹配:规则中多处集合名称与实际路径不符,导致权限验证逻辑根本未触发:
- 实际组织集合路径是
/organizations,但规则写为/organization(单数) - 实际用户集合路径是
/users,但规则中读取用户文档时写为/user(单数) - 实际群组子集合路径是
/organizations/{orgId}/groups,但规则写为/group(单数)
- 实际组织集合路径是
- 集合查询的性能限制:即使修正路径,使用
get()读取用户文档的规则在集合查询时会被Firestore拒绝——Firestore无法为集合中的每个文档都执行一次get()操作(存在性能瓶颈)。
修正后的安全规则
首先修正所有路径错误,同时优化规则逻辑,避免重复读取用户文档:
rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { // 封装用户组织ID的获取逻辑,避免重复调用get() function getUserOrganization() { return get(/databases/$(database)/documents/users/$(request.auth.uid)).data.organization_id; } // 匹配实际的organizations集合 match /organizations/{organizationId} { allow create: if request.auth != null; allow read, update: if getUserOrganization() == organizationId; // 匹配实际的groups子集合 match /groups/{groupId} { allow read, write: if getUserOrganization() == organizationId; } } match /users/{userId} { allow create, read: if request.auth != null; allow update: if get(/databases/$(database)/documents/users/$(request.auth.uid)).data.is_global_admin == true; } } }
进一步优化(解决集合查询性能问题)
上述规则虽能解决路径问题,但集合查询仍可能存在性能限制。推荐将用户的organization_id存入Firebase Auth的自定义声明中,这样无需读取用户文档即可验证权限:
1. 设置自定义声明(后端执行,如Cloud Functions)
const admin = require('firebase-admin'); admin.initializeApp(); // 用户登录或组织变更时执行 async function setUserOrganizationClaim(uid, organizationId) { await admin.auth().setCustomUserClaims(uid, { organization_id: organizationId }); }
2. 更新安全规则
rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { // 直接从Auth自定义声明获取组织ID,无需读取用户文档 function getUserOrganization() { return request.auth.token.organization_id; } match /organizations/{organizationId} { allow create: if request.auth != null; allow read, update: if getUserOrganization() == organizationId; match /groups/{groupId} { allow read, write: if getUserOrganization() == organizationId; } } match /users/{userId} { allow create, read: if request.auth != null; allow update: if request.auth.token.is_global_admin == true; } } }
验证查询代码
你的查询代码无需修改,但需确保传入的org参数是用户所属的组织ID——规则会自动拦截不属于用户组织的查询请求。
内容的提问来源于stack exchange,提问作者Chris Edgington
相关产品推荐
相关产品推荐

