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
相关产品推荐
相关产品推荐

