You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Firebase Firestore安全规则修复求助:聊天通知跨用户读取权限问题

问题描述

我开发了一款简易应用,用户A可向用户B发送聊天、点赞、评论类通知,用户B可接收并读取这些通知。Firestore数据库集合结构如下:

/Notifications/{userId}/NotificationItems/{notificationId}

其中Notifications和NotificationItems为集合,userId是接收通知的用户ID,notificationId是存储通知信息的文档。

最初配置的Firestore规则如下:

match /Notifications/{userId}{
      match /NotificationItems/{notificationId} {
         allow create: if isUserAuthenticated();
         allow read: if isUserAuthenticated() && userOwnsResource(userId);
         allow update: if isUserAuthenticated() && userOwnsResource(userId);
      }
    }

配套函数:

function isUserAuthenticated() {
      return request.auth.uid != null;
    }

    function userOwnsResource(userId) {
      return request.auth.uid == userId;
    }

这些规则支持通知的获取、发送、标记已读等操作,但发送chat类型通知时,需要先检查是否曾向该用户发送过同聊天的通知(间隔过短则不发送),对应的Firestore调用因权限不足失败:

await firebase
    .firestore()
    .collection("Notifications")
    .doc(userId)
    .collection("NotificationItems")
    .where("chatId", "==", chatId)
    .orderBy("date", "desc")
    .get()

尝试修改规则添加itIsChatNotification函数,但未生效:

function itIsChatNotification(){
                let chatId =  get(/databases/$(database)/documents/Notifications/$(userId)/NotificationItems/$(notificationId)).data.chatId;
            return request.auth.uid in get(/databases/$(database)/documents/Chats/$(chatId)).data.users;
         }

修改后的读取规则:

allow read: if isUserAuthenticated() && (userOwnsResource(userId) || itIsChatNotification());
解决方案

问题出在原itIsChatNotification函数的实现逻辑:查询操作是批量读取NotificationItems集合,此时notificationId是通配符,无法通过get()获取具体文档的chatId。需要针对查询操作和单个文档读取分别处理,同时确保客户端查询的约束与规则匹配。

正确的Firestore规则

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    // 验证用户已登录
    function isAuthenticated() {
      return request.auth != null && request.auth.uid != null;
    }

    // 用户是通知的接收者
    function isNotificationRecipient(userId) {
      return request.auth.uid == userId;
    }

    // 验证当前用户是指定聊天的成员
    function isChatMember(chatId) {
      return exists(/databases/$(database)/documents/Chats/$(chatId)) &&
             request.auth.uid in get(/databases/$(database)/documents/Chats/$(chatId)).data.users;
    }

    match /Notifications/{userId}/NotificationItems/{notificationId} {
      // 创建通知:登录用户可操作,chat类型需验证发送方是聊天成员
      allow create: if isAuthenticated() && 
                     (request.resource.data.type != "chat" || 
                      isChatMember(request.resource.data.chatId));
      
      // 读取通知:两种合法场景
      // 1. 用户是通知接收者;2. 读取chat类型通知时,用户是对应聊天成员
      allow read: if isAuthenticated() && 
                   (isNotificationRecipient(userId) || 
                    (resource.data.type == "chat" && isChatMember(resource.data.chatId)));
      
      // 更新通知:仅接收者可操作(标记已读等)
      allow update: if isAuthenticated() && isNotificationRecipient(userId);
    }
  }
}

客户端查询要求

调用查询时必须添加type的过滤条件,否则规则会拒绝请求:

await firebase
    .firestore()
    .collection("Notifications")
    .doc(userId)
    .collection("NotificationItems")
    .where("type", "==", "chat") // 必须添加该条件匹配规则约束
    .where("chatId", "==", chatId)
    .orderBy("date", "desc")
    .get()

规则说明

  1. 创建通知:新增chat类型通知的成员验证,避免非聊天成员恶意发送通知。
  2. 读取通知:
    • 单个文档读取:若为chat类型,验证用户是对应聊天成员即可读取;
    • 批量查询:通过resource.data直接获取文档字段做验证,适配查询时的通配符场景。
  3. 更新通知:仅保留接收者可操作的规则,符合业务逻辑。

内容的提问来源于stack exchange,提问作者newbie coder

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 04:38:21