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

Firestore安全规则配置:基于数组交集限制applications访问

解决Firestore Applications集合的访问规则问题

问题分析

你需要实现的规则逻辑是:用户可访问自己创建的applications,或matchedTenantProfileIds数组中包含自己创建的tenantPreferences文档ID的applications。之前的ownsTenantProfile函数未生效,核心原因是列表查询(list)的规则无法遍历文档数组元素进行逐个验证——Firestore在处理批量查询时,要求规则必须与查询条件直接匹配,而非逐个检查文档内容。

正确的规则实现

以下是结合你的需求和查询逻辑优化后的规则:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    
    // 工具函数:验证用户已认证
    function isAuthenticated() {
      return request.auth != null;
    }

    // 工具函数:验证当前文档属于用户
    function isApplicationOwner() {
      return resource.data.userId == request.auth.uid;
    }

    // 工具函数:验证某个tenantProfile ID属于当前用户
    function ownsTenantProfile(profileId) {
      const profileDoc = /databases/$(database)/documents/tenantPreferences/$(profileId);
      return exists(profileDoc) && get(profileDoc).data.userId == request.auth.uid;
    }

    // 工具函数:验证查询条件中的profile ID属于用户(适配array-contains/array-contains-any)
    function queryUsesValidTenantProfile() {
      // 处理array-contains-any批量查询
      const batchFilter = request.query.where.filter('matchedTenantProfileIds', 'array-contains-any');
      if (batchFilter != null) {
        return exists([id in batchFilter.value], ownsTenantProfile(id));
      }
      // 处理单个array-contains查询
      const singleFilter = request.query.where.filter('matchedTenantProfileIds', 'array-contains');
      if (singleFilter != null) {
        return ownsTenantProfile(singleFilter.value);
      }
      return false;
    }

    // 工具函数:验证当前文档的matchedTenantProfileIds中存在用户拥有的profile(仅用于单个文档读取)
    function documentHasValidTenantProfile() {
      return exists([profileId in resource.data.matchedTenantProfileIds], ownsTenantProfile(profileId));
    }

    // Applications集合规则
    match /applications/{applicationId} {
      // 单个文档读取:允许所有者访问,或文档包含用户的tenantProfile
      allow read: if isAuthenticated() && (isApplicationOwner() || documentHasValidTenantProfile());
      // 列表查询:允许所有者查询,或查询条件使用了用户的tenantProfile ID
      allow list: if isAuthenticated() && (isApplicationOwner() || queryUsesValidTenantProfile());
      
      // 你原本的创建/管理规则可以保留在这里
      allow create, update, delete: if isAuthenticated() && isApplicationOwner();
    }
  }
}

后端查询的适配优化

你的原查询仅针对单个id进行array-contains,但用户可能拥有多个tenantPreferences,建议先获取用户所有的tenantProfile ID,再用array-contains-any批量查询(最多支持10个ID):

// 1. 获取当前用户的所有tenantPreferences文档ID
const tenantPrefsQuery = query(
  collection(db, 'tenantPreferences'),
  where('userId', '==', currentUser.uid)
);
const tenantPrefsSnapshot = await getDocs(tenantPrefsQuery);
const userProfileIds = tenantPrefsSnapshot.docs.map(doc => doc.id);

// 2. 查询包含用户任何一个tenantProfile ID的applications
const applicationsQuery = query(
  collection(db, 'applications'),
  where('matchedTenantProfileIds', 'array-contains-any', userProfileIds)
);
const applicationsSnapshot = await getDocs(applicationsQuery);
const matchedApplicationsData = applicationsSnapshot.docs.map(doc => ({ 
  id: doc.id, 
  ...doc.data() 
} as Application));

关键注意事项

  1. 列表查询规则限制:Firestore不允许在list规则中遍历文档数组元素(如matchedTenantProfileIds)进行逐个验证,必须通过查询条件传递明确的ID,让规则直接验证这些ID的归属。
  2. 性能与配额:get和exists操作会占用Firestore规则的配额,单个文档读取时的documentHasValidTenantProfile函数会遍历数组并逐个检查,若数组过大可能触发配额限制,建议控制matchedTenantProfileIds的长度。
  3. 查询一致性:后端查询必须与规则逻辑匹配——如果用户未在查询中指定自己的tenantProfile ID,规则会直接拒绝列表请求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 00:23:18