注册流程中未认证用户无需Cloud Functions读取Firestore数据的方法
解决方案:无Cloud Functions的React Native Expo Firebase注册安全校验
核心思路:调整注册顺序,利用Auth身份做安全校验
未认证状态下直接读取Firestore确实存在恶意枚举用户名/邮箱的风险。我们可以调整注册流程,先创建Firebase Auth用户,再依托该用户的临时认证身份完成唯一性校验与文档写入,同时通过Firestore安全规则严格限制数据访问权限。
调整后的注册流程
- 用户填写表单并通过本地格式校验
- 调用
createUserWithEmailAndPassword创建Auth用户 - 用新生成的Auth用户身份,查询Firestore检查用户名/邮箱是否已存在
- 若存在:删除刚创建的Auth用户,返回错误提示给用户
- 若不存在:以Auth用户UID为文档ID,在Users集合写入用户信息
Firestore安全规则配置
配置规则确保只有已认证用户可访问唯一性校验查询,且仅能写入自身UID对应的文档:
rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /users/{userId} { // 仅允许已认证用户查询目标用户名/邮箱的存在性,避免恶意枚举 allow read: if request.auth != null && (request.query.where[0].value == resource.data.username || request.query.where[0].value == resource.data.email); // 仅允许用户创建自身UID对应的文档,且文档需不存在 allow create: if request.auth != null && request.auth.uid == userId && !exists(/databases/$(database)/documents/users/$(userId)); } } }
React Native代码实现示例
import { getAuth, createUserWithEmailAndPassword, deleteUser } from "firebase/auth"; import { getFirestore, query, where, collection, getDocs, setDoc, doc } from "firebase/firestore"; const auth = getAuth(); const db = getFirestore(); const signUp = async (username, email, password) => { try { // 第一步:创建Auth用户 const userCredential = await createUserWithEmailAndPassword(auth, email, password); const currentUser = userCredential.user; // 检查用户名唯一性 const usernameQuery = query(collection(db, "users"), where("username", "==", username)); const usernameSnapshot = await getDocs(usernameQuery); if (!usernameSnapshot.empty) { await deleteUser(currentUser); throw new Error("用户名已被注册"); } // 检查邮箱唯一性(可选:Firebase Auth本身会校验邮箱唯一性,若业务允许同一邮箱多账号则保留) const emailQuery = query(collection(db, "users"), where("email", "==", email)); const emailSnapshot = await getDocs(emailQuery); if (!emailSnapshot.empty) { await deleteUser(currentUser); throw new Error("邮箱已被注册"); } // 写入用户文档 await setDoc(doc(db, "users", currentUser.uid), { username, email, createdAt: new Date() // 其他需要存储的用户信息 }); return { success: true, message: "注册成功" }; } catch (error) { // 处理Auth原生错误,比如邮箱已被占用 if (error.code === "auth/email-already-in-use") { return { success: false, message: "邮箱已被注册" }; } return { success: false, message: error.message }; } };
额外注意事项
- 本地表单校验要提前完成(比如邮箱格式、用户名长度限制),减少不必要的Auth请求
- 删除Auth用户时,需确保用户处于登录状态(刚创建的用户默认登录,无需额外处理)
内容的提问来源于stack exchange,提问作者Damjan
相关产品推荐
相关产品推荐

