自定义Claims与Firebase Firestore安全规则权限错误排查求助
问题描述
完成前端开发后,搭建基于Custom Claims的后端安全规则体系,运行Callable Functions并重新登录后,尝试更新Firestore中属于自身的images集合文档时仍报错。规则设定为文档所有者或管理员可进行修改。
相关代码
前端提交代码
const onSubmit = async () => { const editRef = doc(db, `images/${imageID}`); try { await updateDoc(editRef, { aLabels: aLabels, }); } catch (error) { console.log(error.message); toast.error("Something went wrong"); } }
Admin Claims配置代码
const functions = require("firebase-functions"); const admin = require("firebase-admin"); exports.setAdminClaims = functions.https.onRequest(async (req, res) => { const adminUIDs = [ "pyqPlr3BT7LwzxBerxi1seqDrv6x" ]; await Promise.all( adminUIDs.map((uid) => admin.auth().setCustomUserClaims(uid, { admin: true }) ) ); res.send("Done configuring rules"); });
Firestore安全规则
rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /{document=**} { allow read, write: if false; //line 5 } match /images/{image} { allow read: if true; allow create: if isLoggedIn() //line 16 allow update, delete: if isLoggedIn() && isOwner() || isAdmin() } function isLoggedIn() { return request.auth.uid != null; } function isOwner() { return request.auth.uid == resource.data.user } function isAdmin() { return request.auth.token.role.admin == true } }
错误信息
PERMISSION_DENIED: false for 'update' @ L5, evaluation error at
L16:32 for 'update' @ L16, false for 'update' @ L5, Property role is
undefined on object. for 'update' @ L16
问题分析与修复
1. 规则匹配顺序错误
Firestore规则是从上到下匹配,一旦命中就停止。现在全局规则match /{document=**}设置了全拒绝,这个规则会先于images集合的规则生效,直接拦掉所有操作。必须把全局规则移到所有特定集合规则的后面,作为默认兜底。
2. Admin权限判断字段不匹配
在isAdmin()函数里写了request.auth.token.role.admin,但实际在adminClaims代码中设置的Custom Claims是{ admin: true },根本没有role这个层级,所以会报“Property role is undefined”的错误。
3. 逻辑运算符优先级问题
isLoggedIn() && isOwner() || isAdmin()的逻辑有问题,因为&&优先级比||高,实际执行逻辑是(已登录且是所有者) 或 (是管理员),如果你的需求是“已登录,且(是所有者或管理员)”,必须加括号调整优先级。
修复后的安全规则
rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { // 先定义images集合的规则 match /images/{image} { allow read: if true; allow create: if isLoggedIn(); allow update, delete: if isLoggedIn() && (isOwner() || isAdmin()); } // 全局规则放在最后,作为默认拒绝 match /{document=**} { allow read, write: if false; } function isLoggedIn() { return request.auth.uid != null; } function isOwner() { return request.auth.uid == resource.data.user; } function isAdmin() { // 对应Custom Claims里的admin字段 return request.auth.token.admin == true; } }
额外检查事项
- 确认用户重新登录后,Custom Claims已生效:前端可以调用
getAuth().currentUser.getIdTokenResult()查看token中的claims字段。 - 确认
images文档中的user字段确实是当前登录用户的UID,字段名要和规则里的resource.data.user完全一致。
内容的提问来源于stack exchange,提问作者Coolkid

