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));
关键注意事项
- 列表查询规则限制:Firestore不允许在list规则中遍历文档数组元素(如
matchedTenantProfileIds)进行逐个验证,必须通过查询条件传递明确的ID,让规则直接验证这些ID的归属。 - 性能与配额:
get和exists操作会占用Firestore规则的配额,单个文档读取时的documentHasValidTenantProfile函数会遍历数组并逐个检查,若数组过大可能触发配额限制,建议控制matchedTenantProfileIds的长度。 - 查询一致性:后端查询必须与规则逻辑匹配——如果用户未在查询中指定自己的tenantProfile ID,规则会直接拒绝列表请求。
内容的提问来源于stack exchange,提问作者Aerodynamika
相关产品推荐
相关产品推荐

