Firestore规则通配符能否支持列表查询与快照监听器?
Firestore安全规则:Get请求正常但List请求失败问题
数据库结构
/parent_collection /parent_doc ...data, [status]: Enum /sub_collection1 /sub_doc1 ...data /sub_doc2 ...data /sub_collection2 /sub_doc1 ...data /sub_doc2 ...data
当前安全规则
function isPublished(docId) { let data = get(/databases/$(database)/documents/parent_collection/$(docId)).data; return 'status' in data && data.status == "published"; } match /parent_collection/{docId}/{document=**} { // Deny all requests to create, update, or delete the doc allow create, update, delete: if false // Allow the requestor to read the doc if published allow read: if isPublished(docId) }
客户端查询代码
可正常通过规则的Get请求
const getDocById: GetDocById = (docId) => { return new Promise((resolve, reject) => { getDoc(doc(firestore, "parent_collection", docId)) .then((doc) => { resolve(doc.data()); }) .catch((error) => reject(error)); }); };
被规则拒绝的集合List请求
const getDocsWithCursor: GetDocsWithCursor = (cursor) => { const docs: Doc[] = []; return new Promise((resolve, reject) => { let q = query( collection(firestore, "parent_collection"), where("status", "==", "published"), orderBy("updated") ); if (cursor) q = query(q, startAfter(cursor)); getDocs(q) .then((snapshot) => { snapshot.forEach((doc) => { docs.push(doc.data()); }); return resolve([docs, snapshot.docs[snapshot.docs.length - 1]]); }) .catch((error) => { return reject(error); }); }); };
被规则拒绝的子集合快照监听器
const attachCollectionListener: AttachCollectionListener = (docId, callback) => { return onSnapshot( query( collection( firestore, "parent_collection", docId, "sub_collection1" ), orderBy("order") ), (snapshot) => { snapshot.docChanges().forEach((change) => { callback( change.type, change.doc.data(), change.newIndex ); }); } ); };
已排查的失败原因
- 集合级List查询未指定具体文档ID,规则中的通配符
{docId}无法获取有效值,导致isPublished(docId)函数无法执行;而Get请求因明确指定文档ID,通配符能正常取值。 - Firestore安全规则对List查询采用批量校验,不会逐个检查文档权限,要求查询条件必须与规则限制完全匹配,否则整个查询会被拒绝。
问题解答
1. 依赖通配符的规则能否支持集合List查询?
不能直接用原规则实现。集合List查询针对整个parent_collection发起,规则中的{docId}通配符无对应值,isPublished(docId)无法通过get()获取具体文档状态。
可通过调整规则,让集合List查询的校验逻辑与客户端查询条件对齐:
// 单独定义父集合规则 match /parent_collection/{docId} { allow create, update, delete: if false; // 单个文档Get请求校验 allow get: if resource.data.status == "published"; // 集合List请求校验:要求查询必须包含status等于published的条件 allow list: if request.query.where("status", "==", "published"); } // 子集合规则保持原逻辑 match /parent_collection/{docId}/{document=**} { allow create, update, delete: if false; allow read: if get(/databases/$(database)/documents/parent_collection/$(docId)).data.status == "published"; }
客户端的getDocsWithCursor查询已包含where("status", "==", "published")条件,调整后即可通过规则校验。
2. 调整规则支持子集合快照监听器
可以实现。子集合监听器属于List类型请求,但路径中的{docId}由客户端传入、取值明确,规则可正常通过get()获取父文档状态。
使用以下优化后的规则即可:
match /parent_collection/{docId} { allow create, update, delete: if false; allow get: if resource.data.status == "published"; allow list: if request.query.where("status", "==", "published"); } // 子集合及以下的规则 match /parent_collection/{docId}/{subColl}/{subDoc=**} { allow create, update, delete: if false; // 直接校验父文档的published状态 allow read: get(/databases/$(database)/documents/parent_collection/$(docId)).data.status == "published"; }
子集合的所有读请求(包括快照监听器)都会校验对应父文档的status是否为published,只要父文档符合条件,监听器就能正常工作,实现实时同步子集合内容。
内容的提问来源于stack exchange,提问作者Bernard
相关产品推荐
相关产品推荐

