在我的 Firebase Firestore 规则中,我对“类(class)”集合有以下规则:
rules_version = '2'
service cloud.firestore {
match /databases/{database}/documents {
match /courses/{courseId} {
allow read: if resource.data.userId == request.auth.uid;
}
}
}
如果使用 firebase Web 客户端读取单个文档,则规则可以正常工作。 但查询时结果为空,例如下面的查询:
const results = await app.firestore().collection('courses')
.where('userId', '==', 'unexistingUserId').get();
然后规则失败,在客户端抛出异常(缺少或权限不足)。
在模拟器上本地运行时,我发现问题在于读取规则正在针对包含查询字段的资源进行验证,即使该资源不存在:
如上所示,resource.data.userId 包含“unexistingUserId”值。
为了解决这个问题,我必须通过添加验证来检查资源是否存在来更改规则:
rules_version = '2'
service cloud.firestore {
match /databases/{database}/documents {
match /courses/{courseId} {
allow read: if resource==null || resource.data==null || !('id' in resource) || resource.data.userId == request.auth.uid;
}
}
}
现在,当查询没有结果时,规则验证正常,因为请求对象中不存在“id”:
所以我的问题是:
- 这是预期的行为吗?
- 如果查询没有返回任何内容,难道规则不应该被验证吗?
- 有没有办法改变这种行为,以避免在我的所有规则中添加这些检查?
最佳答案
Is this the expected behavior?
Shouldn't the rules not even being validated if the query returns nothing?
这是预期的行为。您所观察到的事实是 security rules are not filters 。请务必阅读并理解链接的文档。
查询的工作方式与单个文档 get() 不同。对于单个文档 get(),规则系统不必担心扩展以匹配数十亿个文档,因此检查一个文档的内容以查看其是否匹配是完全合理的。但是,由于查询可以返回 0 个或多个文档,因此对规则的考虑有所不同。他们并不简单地检查查询产生的每个文档。对于非常大的集合来说,这根本无法扩展。
规则通过检查客户端查询上的过滤器是否与规则中的约束匹配来处理查询。如果你这么说resource.data.userId == request.auth.uid
,您所表达的是用户可能仅尝试查询 userId 等于其自己的 UID 的文档。他们甚至不允许尝试匹配查询中的任何其他文档。
唯一能够通过此规则的查询看起来更像是这样:
const results = await app.firestore()
.collection('courses')
.where('userId', '==', firebase.auth().currentUser)
.get();
在这种情况下,过滤器:
.where('userId', '==', firebase.auth().currentUser)
完全符合规则:
allow read: if resource.data.userId == request.auth.uid;
任何其他查询都会被立即拒绝,因为它试图访问已知违反规则的文档。
关于即使在查询结果中找不到文档,Firebase "read"规则也会执行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62917493/