Firebase规则配置:读写权限分离及CRUD应用权限问题解决
问题1:构建读取权限为true、写入权限为false的Firebase规则并获取数据
要实现某个节点仅允许读取、禁止写入的权限,你只需要在Firebase规则中给该节点设置.read: true和.write: false即可。比如针对一个publicData节点:
{ "rules": { "publicData": { ".read": true, ".write": false } } }
设置好规则后,你可以通过Firebase SDK正常读取数据,比如在JavaScript中:
// 获取publicData节点的数据 database.ref('publicData').once('value') .then(snapshot => { const data = snapshot.val(); console.log('获取到的数据:', data); }) .catch(error => { console.error('读取数据失败:', error); });
注意:如果尝试写入该节点,Firebase会直接返回权限拒绝错误,符合预期。
问题2:解决CRUD应用的Firebase权限拒绝错误与优化方案
先帮你拆解当前遇到的问题,再给出适配需求的优化方案:
问题根源分析
- 规则与代码路径不匹配:你的
posts节点规则要求只有匹配Set_User_Id的用户才能写入posts/$user_id路径,但你的Redux Action是直接push到posts根节点(生成随机ID的子节点),完全不符合规则的路径要求,自然触发权限拒绝。 - 硬编码用户ID:规则里用了固定的
'Set_User_Id',而非动态获取当前登录用户的auth.uid,这会导致只有这个硬编码ID的用户能操作,无法适配实际的登录用户体系。 - 并行写入写法错误:你用
&&连接两个Promise操作,这不是正确的异步并行处理方式,可能导致操作顺序混乱或错误捕获不完整。
优化后的Firebase规则
根据你的需求(指定登录用户可创建内容,所有用户<登录/未登录>均可读取),调整规则如下:
{ "rules": { "users": { "$user_id": { // 仅当前登录用户可读写自己的节点 ".read": "$user_id === auth.uid", ".write": "$user_id === auth.uid", "posts": { "$post_id": { // 验证帖子必填字段和格式 ".validate": "newData.hasChildren(['title', 'postType', 'image', 'createdAt', 'note', 'link'])", "title": { ".validate": "newData.isString() && newData.val().length > 0" }, "postType": { ".validate": "newData.isString()" }, "createdAt": { ".validate": "newData.isNumber()" }, "image": { ".validate": "newData.isString()" }, "link": { ".validate": "newData.isString()" }, "note": { ".validate": "newData.isString()" }, "$other": { ".validate": false } // 禁止额外字段 } }, "$other": { ".validate": false } } }, "posts": { // 所有用户均可读取 ".read": true, // 仅已登录用户可创建帖子 ".write": "auth != null", "$post_id": { // 验证帖子必须包含创建者uid,且与当前登录用户一致 ".validate": "newData.hasChild('uid') && newData.child('uid').val() === auth.uid", // 复用字段验证规则 "title": { ".validate": "newData.isString() && newData.val().length > 0" }, "postType": { ".validate": "newData.isString()" }, "createdAt": { ".validate": "newData.isNumber()" }, "image": { ".validate": "newData.isString()" }, "link": { ".validate": "newData.isString()" }, "note": { ".validate": "newData.isString()" }, "$other": { ".validate": false } } } } }
修正后的Redux Action代码
- 用
Promise.all并行处理两个写入操作,确保所有操作完成后再分发action; - 在帖子数据中加入当前用户的
uid,满足posts节点的验证规则; - 统一错误捕获,方便排查问题。
export const startAddPost = (postData = {}) => { return (dispatch, getState) => { const uid = getState().auth.uid; const { title = '', postType = '', image = '', createdAt = 0, note = '', link = '' } = postData; // 加入uid字段,用于Firebase规则验证 const post = { title, postType, image, createdAt, note, link, uid }; // 并行执行两个写入操作 return Promise.all([ database.ref(`users/${uid}/posts`).push(post), database.ref('posts').push(post) ]).then(([userPostRef, publicPostRef]) => { // 可根据需求选择分发一个或两个action dispatch(addPost({ id: userPostRef.key, ...post })); dispatch(addPost({ id: publicPostRef.key, ...post })); }).catch(error => { console.error('写入帖子失败:', error); // 可添加用户通知等错误处理逻辑 throw error; }); }; };
额外优化建议
如果不需要在users/${uid}/posts和posts节点保存两份完全相同的数据,可以考虑只在posts节点保存公开帖子,然后在users/${uid}/posts中只保存帖子ID引用(比如{ postId: 'xxx' }),减少数据冗余:
return database.ref('posts').push(post).then(publicPostRef => { // 仅在用户节点保存帖子ID return database.ref(`users/${uid}/posts/${publicPostRef.key}`).set(true); }).then(() => { dispatch(addPost({ id: publicPostRef.key, ...post })); });
对应的用户节点规则也可以简化,只需要验证是否为合法的ID引用即可。
内容的提问来源于stack exchange,提问作者Tony
相关产品推荐
相关产品推荐

