Firebase Storage文件夹列表权限失效:无元数据时如何授权?
嘿,咱们先搞清楚为啥你现在的规则会失效:
你调用listAll()是针对/{groupId}/Images这个文件夹路径的,但当前的安全规则只匹配到该路径下的文件(/{groupId}/Images/{allPaths=**})。而Firebase Storage里的文件夹其实是虚拟的——根本不存在实际的存储对象,自然也就没有元数据。所以当执行list操作时,规则里的resource对象是空的,resource.metadata.keys()这部分直接就导致权限校验失败了。
你提到注册时没法获知groupId,没法用自定义令牌,那咱们换个靠谱的方案:结合Firebase数据库(不管是Realtime DB还是Firestore)存储群组的成员关系,然后在Storage规则里读取这些数据来做权限校验。
第一步:先把群组成员关系存到数据库里
假设用Firestore的话,创建一个groups集合,每个群组的文档ID就是groupId,文档里加个members字段,存所有成员的UID数组就行:
// Firestore文档示例结构 groups/{groupId} { members: ["你的用户UID1", "你的用户UID2"] }
要是用Realtime Database,结构可以这样:
{ "groups": { "groupId1": { "members": { "UID1": true, "UID2": true } } } }
第二步:修改Storage安全规则
根据你用的数据库类型,调整规则来验证用户是不是该群组的成员:
适配Firestore的规则
rules_version = '2'; service firebase.storage { match /b/{bucket}/o { // 专门给Images文件夹的list操作加权限:用户必须是群组成员 match /{groupId}/Images { allow list: if exists(/databases/(default)/documents/groups/$(groupId)) && request.auth.uid in get(/databases/(default)/documents/groups/$(groupId)).data.members; } // 文件的读写权限可以保留你原来的元数据校验,也可以换成数据库校验 match /{groupId}/Images/{allPaths=**} { allow read: if request.auth.uid in resource.metadata.keys() && groupId in resource.metadata.keys(); allow write: if request.auth.uid in request.resource.metadata.keys() && groupId in request.resource.metadata.keys(); // 要是想统一用数据库校验,也可以换成下面这行 // allow read, write: if exists(/databases/(default)/documents/groups/$(groupId)) && request.auth.uid in get(/databases/(default)/documents/groups/$(groupId)).data.members; } } }
适配Realtime Database的规则
rules_version = '2'; service firebase.storage { match /b/{bucket}/o { match /{groupId}/Images { // 检查用户UID是否在群组的members节点里 allow list: if exists(/databases/(default)/groups/$(groupId)/members/$(request.auth.uid)); } match /{groupId}/Images/{allPaths=**} { allow read: if request.auth.uid in resource.metadata.keys() && groupId in resource.metadata.keys(); allow write: if request.auth.uid in request.resource.metadata.keys() && groupId in request.resource.metadata.keys(); // 同样可以换成Realtime DB的成员校验 // allow read, write: if exists(/databases/(default)/groups/$(groupId)/members/$(request.auth.uid)); } } }
关于你问的「能不能访问文件夹下任意图片的元数据来授权」
很遗憾,这在Firebase Storage的安全规则里是做不到的。安全规则是针对单个请求上下文的——当你请求list文件夹时,规则只能访问当前请求的目标(也就是那个虚拟文件夹,没元数据),根本没法遍历或者读取文件夹下其他文件的元数据来做校验。所以必须依赖外部数据库来存群组的权限关系,这是最稳妥的办法。
额外优化建议
如果你想保留元数据的校验,可以在写入规则里同时做双重校验:既验证用户是群组成员,又确保元数据里的groupId和uid是正确的,这样安全性更高:
allow write: if request.auth.uid in request.resource.metadata.keys() && groupId in request.resource.metadata.keys() && exists(/databases/(default)/documents/groups/$(groupId)) && request.auth.uid in get(/databases/(default)/documents/groups/$(groupId)).data.members;
这样一来,list操作的权限问题解决了,文件的读写权限也更安全。
内容的提问来源于stack exchange,提问作者Komal Chugh

