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

Cloud Firestore权限拒绝问题:安全规则与查询逻辑不匹配排查

Cloud Firestore安全规则permission-denied问题解决

问题现象

调用getDocumentsForGroup方法查询group_ids数组包含指定groupId的Document集合时,触发permission-denied错误。即使当前用户UID与文档user_id完全一致,甚至集合为空时,错误依然存在。

代码片段

Dart查询代码

Future<List<Document>> getDocumentsForGroup(String groupId) async {
  final user = FirebaseAuth.instance.currentUser;
  if (user == null) {
    throw Exception('User not authenticated');
  }
  try {
    final querySnapshot = await FirebaseFirestore.instance
        .collection('Document')
        .where('group_ids', arrayContains: groupId)
        .get();

    return querySnapshot.docs.map((doc) {
      final data = doc.data();
      return Document(
        // ... 你的对象映射逻辑
      );
    }).toList();
  } catch (e) {
    throw Exception('Error loading the documents: $e');
  }
}

当前安全规则

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /Document/{document} {
      allow read, delete, get: if request.auth.uid == resource.data.user_id;
      allow create: if request.auth.uid == request.resource.data.user_id;
      allow update: if request.auth.uid == request.resource.data.user_id
                    && request.auth.uid == resource.data.user_id;
    }
  }
}

问题原因

Firestore安全规则对**集合查询(list操作)**和单个文档读取(get操作)的验证逻辑不同:

  • 单个文档读取可以直接校验resource.data.user_id是否匹配当前用户;
  • 但集合查询时,Firestore无法逐个扫描文档验证权限(受性能限制),它要求规则能提前验证查询条件本身符合权限约束,确保返回的所有文档都满足权限要求。

当前规则中allow read包含了list操作,但规则依赖resource.data.user_id,这导致Firestore无法验证查询的合法性,直接拒绝请求。

生产环境合规解决方案

方案一:用户专属文档查询(仅所有者可访问)

适合文档仅归创建者所有,仅允许所有者查询自己的组文档场景。

修改Dart查询代码

添加用户ID过滤条件,确保查询只返回当前用户的文档:

Future<List<Document>> getDocumentsForGroup(String groupId) async {
  final user = FirebaseAuth.instance.currentUser;
  if (user == null) {
    throw Exception('User not authenticated');
  }
  try {
    final querySnapshot = await FirebaseFirestore.instance
        .collection('Document')
        .where('user_id', isEqualTo: user.uid) // 新增:过滤当前用户的文档
        .where('group_ids', arrayContains: groupId)
        .get();

    return querySnapshot.docs.map((doc) {
      final data = doc.data();
      return Document(
        // ... 你的对象映射逻辑
      );
    }).toList();
  } catch (e) {
    throw Exception('Error loading the documents: $e');
  }
}

修改安全规则

拆分read权限为get(单个文档)和list(集合查询),明确验证查询条件:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /Document/{document} {
      // 单个文档读取:用户是文档所有者
      allow get: if request.auth.uid == resource.data.user_id;
      // 集合查询:查询条件包含当前用户ID,确保返回的都是用户自己的文档
      allow list: if request.auth != null 
                  && request.query.where('user_id', '==', request.auth.uid);
      // 删除权限:仅所有者
      allow delete: if request.auth.uid == resource.data.user_id;
      // 创建权限:用户是文档所有者
      allow create: if request.auth.uid == request.resource.data.user_id;
      // 更新权限:所有者,且禁止修改user_id
      allow update: if request.auth.uid == request.resource.data.user_id
                    && request.auth.uid == resource.data.user_id;
    }
  }
}

方案二:组内多用户访问(支持共享文档)

适合文档属于某个组,组内所有用户都有权限访问的场景。

修改安全规则

验证当前用户是否在文档的group_ids数组中,同时确保查询条件符合规则约束:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /Document/{document} {
      // 单个文档读取:用户是所有者或属于文档所在组
      allow get: request.auth != null 
                 && (request.auth.uid == resource.data.user_id 
                     || request.auth.uid in resource.data.group_ids);
      // 集合查询:查询条件包含当前用户在group_ids中,确保返回的都是用户有权访问的文档
      allow list: request.auth != null 
                  && request.query.where('group_ids', 'array-contains', request.auth.uid);
      // 删除权限:仅文档所有者
      allow delete: if request.auth.uid == resource.data.user_id;
      // 创建权限:用户是所有者,且自己在组内(可选,根据业务需求调整)
      allow create: request.auth != null 
                    && request.auth.uid == request.resource.data.user_id
                    && request.auth.uid in request.resource.data.group_ids;
      // 更新权限:所有者,禁止修改user_id和group_ids(可选,根据业务需求调整)
      allow update: request.auth != null 
                    && request.auth.uid == resource.data.user_id
                    && request.resource.data.user_id == resource.data.user_id
                    && request.resource.data.group_ids == resource.data.group_ids;
    }
  }
}

调整Dart查询代码

添加当前用户在group_ids中的过滤,匹配规则要求:

Future<List<Document>> getDocumentsForGroup(String groupId) async {
  final user = FirebaseAuth.instance.currentUser;
  if (user == null) {
    throw Exception('User not authenticated');
  }
  try {
    final querySnapshot = await FirebaseFirestore.instance
        .collection('Document')
        .where('group_ids', arrayContains: groupId)
        .where('group_ids', arrayContains: user.uid) // 新增:过滤用户有权访问的组文档
        .get();

    return querySnapshot.docs.map((doc) {
      final data = doc.data();
      return Document(
        // ... 你的对象映射逻辑
      );
    }).toList();
  } catch (e) {
    throw Exception('Error loading the documents: $e');
  }
}

关键说明

Firestore安全规则的核心原则是查询条件必须与权限规则对齐,这样Firestore才能在不扫描全集合的情况下验证权限,既保证安全又不影响性能。两种方案都严格遵循这个原则,没有开放全局权限,适合生产环境使用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 14:14:52