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

Firestore安全规则无法读取app_users集合EIDSTRING字段问题排查

问题分析与解决方案

核心问题

你遇到的问题根源在于Firestore查询规则的可索引性要求,以及当前规则逻辑在查询场景下的评估限制:

  • 执行where批量查询时,Firestore无法提前遍历user_contracts所有文档验证resource.data.EIDSTRING,依赖单个文档resource的权限判断会直接失效。
  • isAdminOrSpecialist能正常工作是因为它的调用场景是单个文档操作(如更新app_users),而非批量查询。

具体修复步骤

1. 调整安全规则,适配查询场景

修改user_contracts的读取规则,拆分单个文档读取与批量查询的权限逻辑,确保规则可被Firestore索引验证:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {

    // Match app_users document
    match /app_users/{userId} {
      allow read: if request.auth != null && belongsTo(userId);
      allow update: if isAdminOrSpecialist(userId) || (request.auth != null 
        && belongsTo(userId)
        && !hasRestrictedFields()
      );
      allow create: if request.auth != null;
      allow delete: if false;
    }

    match /user_contracts/{document} {
      // 单个文档读取:验证文档EID与用户EID匹配
      allow get: hasContract(resource.data.EIDSTRING, request.auth.uid);
      // 批量查询:验证查询条件必须指定EID等于用户自身的EID
      allow list: if request.query.where('EIDSTRING', '==', getUserEID(request.auth.uid));
      allow write: if false;
    } 

    match /{document=**}{
      allow read: if request.auth != null;
      allow update: if false;
      allow create: if false;
      allow delete: if false;
    }

    function isAdminOrSpecialist(userId){
      let userDoc = get(/databases/$(database)/documents/app_users/$(userId));
      return userDoc.data.role == 'admin' || userDoc.data.role == 'specialist';
    }

    function getUserEID(userId){
      // 先验证文档存在,避免get失败导致规则中断
      let userDocRef = /databases/$(database)/documents/app_users/$(userId);
      if (!exists(userDocRef)) {
        return '';
      }
      let userDoc = get(userDocRef);
      return userDoc.data.EIDSTRING;
    }

    function hasContract(docEID, userId){
      let userEID = getUserEID(userId);
      return debug(userEID) == debug(docEID);
    }

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

    function hasRestrictedFields(){
      return request.resource.data.diff(resource.data).affectedKeys().hasAny(['EIDSTRING','impersonatedGrower', 'role']);
    }
  }
}

2. 测试代码适配说明

你的现有测试代码已经在查询中指定了EIDSTRING匹配条件,完全符合调整后的规则要求,无需修改。只需确保传入的EIDSTRING与app_users中对应用户的字段值一致即可。

3. 关键细节补充

  • 拆分get与list权限:单个文档读取仍依赖resource验证,批量查询直接校验查询条件与用户EIDSTRING的匹配,让Firestore能通过索引快速完成权限验证,避免遍历所有文档。
  • 文档存在校验:在getUserEID中添加exists判断,避免因用户文档不存在导致规则直接失败,增强规则健壮性。
  • 权限优先级:最底层的match /{document=**}规则会被更具体的user_contracts规则覆盖,无需担心权限冲突。

调试验证

在Firestore规则模拟器中:

  • 选择查询操作,指定user_contracts集合,添加EIDSTRING == [用户的EID值]的条件。
  • 模拟已认证用户(UID对应用户的app_users文档ID)执行测试,此时应能通过权限验证。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 23:07:36