Firestore安全规则-相同的规则适用于写入,但不适用于读取



我正在开发一个使用firestore的Firebase项目。我正在查询Firestore以从消息集合中获取聊天消息。我只想要属于某个对话的消息:

const messages = await db
.collection('messages')
.where('room', '==', room)
.onSnapshot(snap => {//stuff})

所以fa很好。它有效。当我设置安全规则时,出现问题。

如果我做一些简单的事情,比如:

allow read: if request.auth != null;

一切都很好。但是,如果我想只允许uid包含在"partiesIDs"消息对象属性中的用户访问,就会出现问题:

allow read: if
request.auth.uid == resource.data.pertiesIDs[0] ||
request.auth.uid == resource.data.parties[1];

最奇怪的是,我有一个非常相似的更新规则,它按预期工作:

allow update: if
(request.resource.data.diff(resource.data).affectedKeys()
.hasOnly(['read', 'notified'])) &&
(request.auth.token.name == resource.data.parties[0] ||
request.auth.token.name == resource.data.parties[1]);

第二条规则(有效(的查询如下:

const update = await db
.collection('messages')
.doc(docid)
.update({read: true, notified: true})

我被卡住了!有人能揭露这一迷雾吗?

问题是Firestore安全规则不是过滤器。我强烈建议阅读该文件。

当您编写规则以在集合中设置文档读取条件时,客户端必须进行与规则条件完全匹配的查询。当您要求某些字段中必须存在某些数据时,则您的查询必须通过仅筛选满足规则所需字段内容的文档来匹配该查询规则不会只提取匹配的文档来返回它们您可以将客户端查询视为要求与给定筛选器匹配的全套文档,将规则视为因为条件不满足而拒绝该要求。

但是,这里有一点问题,因为查询没有指定数组索引的方法。不可能使查询要求数组字段的索引0必须包含某个值。

我建议重新思考您的文档数据和规则,并以客户端应用程序能够完全匹配其要求的方式对其进行结构化。

已解决!

@道格·史蒂文森感谢你为我指明了正确的方向。

我制定的规则如下:

allow read: if request.auth.token.name == resource.data.receiver || request.auth.token.name == resource.data.sender;
allow create: if request.auth != null;
allow update: if (request.resource.data.diff(resource.data).affectedKeys()
.hasOnly(['read'])) && (request.auth.token.name == resource.data.parties[0] || request.auth.token.name == resource.data.parties[1]);}
allow delete: if false;

有了这些规则,我可以做到以下几点:

  1. 第一次加载:通过云功能加载给定房间的聊天消息(它具有管理员权限,因此没有安全规则问题(

  2. 获取传入消息:添加一个onsnapshot实时侦听器以获取所有消息,其中接收方是登录用户,属性房间等于开放房间

    await db.collection('messages'(.where("receiver","==",user.displayName(.where("room","===",room(.orderBy('timestamp'(.onSnapshot(snapshot=>{//Do Stuff}(

  3. 更新的读取状态:当显示消息时,接收器更新firestore中mssage的读取属性:

    constupdate=await-db.collection("messages"(.doc(id(.update({read:true}(;

  4. 实时在UI中呈现读取状态:添加第二个onsnapshot实时侦听器以获取所有消息,其中发件人是登录用户,房间等于开放房间

    const readUpdate=等待数据库收集('messages'(.其中("sender","==",user.displayName(.其中("房间","==",房间(.orderBy("时间戳"(.onSnapshot(snapshot=>{//Do Stuff}(

  5. 发送消息:用户通过firestore发送新消息。在发送方,新消息是通过JS本地呈现的(而不是通过实时侦听器(。在接收端,通过2(中描述的监听器传递和显示新消息

    const deliveryMSG=await db.collection('messages'(.doc(newID(.set(newMsgObj(;//呈现消息renderMSG({id:newID,data:newMsgObj}(;

也许这不是最优雅的处理方式,但它运行得很好,而且绝对安全。如果你有任何额外的提示,我很乐意阅读。

最新更新