这不是bug,是JavaScript==隐式转换导致的逻辑漏洞;正确写法应为doc.user_id === auth.uid,并确保user_id为标准24位ObjectId字符串。

ClientDB权限校验不是“开了就能用”,必须配对生效:db schema 的 permission 规则 + 正确的用户身份(auth.uid)+ 合法的登录态(uni-id 初始化),三者缺一不可。
为什么 doc.user_id == auth.uid 有时不生效?
这不是 bug,是类型隐式比较导致的逻辑漏洞。当 user_id 字段存的是字符串 "1" 或 "abc",而 auth.uid 是 uni-id 生成的 24 位 ObjectId 字符串(如 "60d17be89dad850001cfaaa9")时,JavaScript 的 == 会尝试类型转换 —— 空字符串、纯数字字符串可能被转成数字再比,结果意外为 true。
- ✅ 正确写法必须用严格相等:
doc.user_id === auth.uid - ✅ 同时确保
user_id字段在 schema 中声明为"bsonType": "string",且实际插入值确实是标准 ObjectId 字符串(不是数字、不是短字符串) - ⚠️ 如果历史数据里混入了非 ObjectId 格式的
user_id(比如后端误写成数字 123),权限规则会失效,需清洗数据
如何验证权限规则是否真正起作用?
别只靠前端报错判断。真实校验发生在云端,前端看到的 permission denied 错误,是服务端拦截后返回的。验证步骤要绕过前端缓存和本地调试干扰:
- 用 HBuilderX 的「uniCloud 控制台 → 数据库 → 表 → 权限配置」直接编辑
permission.read,保存后立刻生效(无需重启或重新部署) - 在控制台「测试」Tab 手动构造请求:选中表 → 点「测试读取」→ 切换不同登录用户(admin / 普通用户)→ 观察返回结果是否符合预期
- 在真机或体验版小程序中操作,禁用「本地数据库」模拟开关(HBuilderX 调试设置里关掉),避免 clientDB 读取本地 mock 数据掩盖问题
auth 对象从哪来?为什么总提示“匿名用户”?
auth 不是前端传的,是 uniCloud 在请求到达时,根据请求头里的 token 自动解析出来的。出问题基本就两类:
- ❌ 未调用
uni.login或uniCloud.callFunction登录流程,uni-id没生成有效 token;检查是否漏了uniIdToken存储或过期 - ❌
uni-id配置错误:确认uni-id插件已安装,且uni-id/config.json中的tokenSecret和云空间密钥一致;uni-id-common场景下,必须通过uniId.init()显式初始化,不能只靠插件自动加载 - ✅ 最小验证法:在页面
onLoad里加一行console.log(uni.getStorageSync('uni_id_token')),有值且长度约 100+ 字符才算登录成功
权限规则里能用哪些变量和函数?
clientDB 的权限表达式运行在服务端沙箱环境,支持有限但够用:
- ✅ 基础变量:
auth.uid(当前用户 ID)、auth.role(角色数组,如["user", "admin"])、doc(当前文档对象) - ✅ 常用函数:
Array.includes()、String.startsWith()、Date.now();注意不能用new Date()或Math.random() - ✅ 复杂权限示例:
auth.role.includes("admin") || (doc.user_id === auth.uid && doc.status !== "deleted") - ⚠️ 不支持异步、不支持自定义函数、不支持数据库查询(比如不能写
db.collection("users").where(...).get())
最常被忽略的一点:权限校验只发生在 get、add、update、delete 这四个操作入口,watch 实时监听也走同一套规则;但 JQL 查询中的 .where() 条件是客户端拼的,不参与权限校验 —— 所以 doc.user_id === auth.uid 必须写在 schema 的 permission.read 里,而不是 JS 代码的 .where() 中。


















