Firestore阻止空对象覆盖文档?安全规则失效问题排查
问题分析与解决方案
首先,你的安全规则里有一个致命的拼写错误,这是导致规则完全失效的核心原因:
你定义的判断函数是objectIsEmpty(),但在allow write的条件里写的是!objectIsNotEmpty()——这个objectIsNotEmpty()函数根本不存在!Firestore安全规则会把未定义的函数视为false,所以!false等价于true,相当于你实际上允许了所有写入操作,这就是为什么set({})能顺利执行,甚至你设置allow write: if false;都没效果(大概率是你修改规则后没正确部署,或者客户端缓存了旧规则,这点后面再细说)。
正确的规则写法
先修正函数名,我们需要明确:要阻止的是写入后文档数据为空对象的操作——不管是创建新的空文档,还是把已有文档覆盖为空。以下是修正后的规则:
service cloud.firestore { match /databases/{database}/documents { // 判断写入后的数据是否为空对象 function isEmptyPayload() { return request.resource.data.size() == 0; } match /users/{userId} { allow read; // 禁止任何写入后数据为空的操作 allow write: if !isEmptyPayload(); } } }
为什么update({})能被拦截,而之前的set({})不行?
本质还是规则的拼写错误导致逻辑完全失效:之前的规则相当于允许所有写入,而当你测试update({})时,可能是你临时修正了判断条件,或者update操作在规则中的request.resource.data处理逻辑让错误的规则碰巧生效了,但核心问题还是规则的拼写错误。
额外注意事项
- 确保规则已正确部署:如果你在控制台修改了规则但没点击「发布」按钮,新规则不会生效,这会让你误以为规则无效。
- 客户端缓存问题:Firestore客户端会缓存安全规则一段时间,修改规则后可能需要等待几分钟,或者重启测试设备/应用才能看到效果。
- 区分set的merge模式:如果客户端代码使用
set({ ... }, { merge: true }),即使传入空对象,也不会覆盖整个文档(merge模式只会更新指定字段,空对象等于不更新),这种情况你的规则不会拦截,但也不会导致文档被清空。
如果还有问题,可以尝试在Firestore控制台的「规则模拟器」中测试set({})操作,直接查看规则的判断结果,能快速定位问题。
内容的提问来源于stack exchange,提问作者Kuba
相关产品推荐
相关产品推荐

