Firestore安全规则优化:邮箱认证用户创建的权限与用户名校验
安全的Firestore用户规则与用户名唯一性实现方案
一、Firestore规则优化(替代原有宽松规则)
原有规则允许所有用户读取、列出全部用户数据,风险极高。以下是遵循最小权限原则的安全规则:
rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { // 默认规则:所有操作需先通过身份认证 match /{document=**} { allow read, write: if request.auth != null; } // 用户集合专属规则 match /users/{userId} { // 仅允许用户读取自己的文档 allow get: if request.auth.uid == userId; // 仅允许用户创建/更新自己的文档,且数据格式合法 allow create, update: if request.auth.uid == userId && validateUser(request.resource.data); // 禁止删除用户文档(可根据需求调整) allow delete: if false; // 仅允许针对用户名的精确查重查询,限制返回1条结果 allow list: if request.query.limit <= 1 && request.query.where('name', '==', request.query.name) != null; } // 验证用户数据格式的辅助函数 function validateUser(data) { return data.name is string && data.name.size() > 0 && data.email is string && data.email.matches('^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$'); } } }
规则核心说明:
- 身份认证前置:所有操作必须基于已登录用户,拦截未授权访问
- 数据隔离:用户仅能读写自己UID对应的文档,避免越权操作他人数据
- 创建/更新校验:强制文档UID与用户认证UID一致,同时验证
name、email字段的合法性 - 受控查询:仅允许用于用户名查重的精确查询,限制返回数量,防止批量爬取用户数据
二、用户名唯一性校验的改进
你当前实现的前端查重仅能提升用户体验,但存在竞态风险(比如两个用户同时查询同一用户名,均通过后提交创建),且前端逻辑可被篡改。必须在服务端做最终校验,推荐使用Firebase云函数实现原子性创建流程:
1. 云函数实现(服务端校验+创建)
const functions = require("firebase-functions"); const admin = require("firebase-admin"); admin.initializeApp(); exports.createUserWithUsername = functions.https.onCall(async (data, context) => { const { email, password, name } = data; // 服务端二次校验用户名是否存在 const usernameQuery = await admin.firestore() .collection('users') .where('name', '==', name) .limit(1) .get(); if (!usernameQuery.empty) { throw new functions.https.HttpsError('already-exists', '用户名已存在'); } // 创建Auth用户 const userRecord = await admin.auth().createUser({ email: email, password: password, displayName: name }); // 写入Firestore用户文档(UID与Auth用户UID绑定) await admin.firestore().collection('users').doc(userRecord.uid).set({ name: name, email: email, createdAt: admin.firestore.FieldValue.serverTimestamp() }); return { uid: userRecord.uid }; });
2. 前端调用调整
替换原有的直接调用createUserWithEmailAndPassword,改为调用云函数:
// 调用云函数创建用户 final HttpsCallable callable = FirebaseFunctions.instance.httpsCallable('createUserWithUsername'); try { final result = await callable.call({ 'email': emailController.text, 'password': passwordController.text, 'name': nameController.text, }); String uid = result.data['uid']; // 后续逻辑... } on FirebaseFunctionsException catch (e) { if (e.code == 'already-exists') { // 提示用户名已存在 } // 处理其他错误 }
改进说明:
- 服务端强校验:避免前端篡改逻辑导致的用户名重复
- 原子性操作:确保Auth用户创建与Firestore文档写入同时成功/失败,避免数据不一致
- 错误标准化:通过云函数的错误码统一处理异常场景
内容的提问来源于stack exchange,提问作者Bernhard
相关产品推荐
相关产品推荐

