如何在Firebase Firestore所有集合的安全规则中获取并校验用户角色
核心问题解答
你需要的跨集合通用角色校验完全可以实现,你现在写的通用函数的思路是对的,定义在match /databases/{database}/documents层级下的函数,所有子集合的规则都能直接调用,不需要重复编写校验逻辑。
你现有规则存在一处明显的逻辑错误:你定义的getRole()函数返回的是布尔值(直接判断了角色是不是等于pädagoge),但后续isOneOfRoles里又把getRole()的返回值放到数组里做存在判断,逻辑完全不匹配。
以下是修正后的可用规则示例:
rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { // 通用校验:用户是否已登录 function isSignedIn() { return request.auth != null; } // 通用工具:获取当前登录用户的角色 function getRole() { // 先判断用户文档是否存在,避免get操作报错 let userDoc = get(/databases/$(database)/documents/users/$(request.auth.uid)); return userDoc.exists ? userDoc.data.role : ''; } // 通用校验:用户是否属于指定角色列表 function isOneOfRoles(allowedRoles) { return isSignedIn() && getRole() in allowedRoles; } // posts集合规则示例,可根据需求调整权限 match /posts/{userPosts} { // 所有登录用户可读 allow read: if isSignedIn(); // 只有teacher(pädagoge)可以创建 allow create: if isOneOfRoles(['pädagoge']); // 扩展示例:teacher可以编辑/删除所有帖子,parent只能编辑自己发布的帖子 allow update, delete: if isOneOfRoles(['pädagoge']) || (request.auth.uid == resource.data.authorId); } // messages集合规则示例 match /messages/{messages} { // 所有登录用户可读 allow read: if isSignedIn(); // teacher和parent都可以发消息,仅teacher可以删除任意消息 allow create: if isOneOfRoles(['pädagoge', 'parent']); // 替换成你实际的parent角色字段值即可 allow delete: if isOneOfRoles(['pädagoge']); } // 其余所有集合都可以直接调用上面的通用函数做权限校验 } }
优化建议
- 上面的实现每次校验角色都会读取一次users集合的文档,会产生少量的读取费用。如果你对性能要求更高、角色变更不频繁,可以把用户角色写到Firebase Auth的自定义声明里,规则里直接通过
request.auth.token.role就能拿到角色,不需要额外读库,性能更好也更节省成本。 - 如果需要支持公开资源的判断,你可以把之前的
openWorld判断加回isOneOfRoles里,注意如果是create操作的话resource还不存在,要根据场景使用request.resource替代。
内容的提问来源于stack exchange,提问作者chin14
相关产品推荐
相关产品推荐

