首页 > 解决方案 > 即使在查询结果中没有找到文档,Firebase 也会执行“读取”规则

问题描述

在我的 Firebase Firestore 规则中,我对“课程”集合有以下规则:

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”: 在此处输入图像描述

所以我的问题是:

标签: firebasegoogle-cloud-firestorefirebase-security

解决方案


这是预期的行为吗?

如果查询没有返回任何内容,规则甚至不应该被验证吗?

这是预期的行为。您正在观察的是因为安全规则不是过滤器这一事实。请务必阅读并理解该链接文档。

查询的工作方式与单个文档 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;

任何其他查询都将立即被拒绝,因为它试图访问已知违反规则的文档。


推荐阅读