基于Firebase与React JS实现手机号+OTP注册及密码登录方案咨询
实现方案概述
Firebase Auth原生不支持手机号+密码直接登录,我们可以通过手机号OTP完成身份认证 + Firestore存储加密后的用户密码的组合方式实现需求:
- 注册阶段:用户输入手机号→获取并验证OTP→验证通过后设置密码→将加密后的密码与用户UID关联存储到Firestore
- 登录阶段:用户输入手机号+密码→从Firestore获取对应手机号的加密密码→比对密码正确性→验证通过后用手机号OTP完成登录(符合Firebase Auth安全规范)
一、Firebase 后端配置
- 启用Firebase Auth的手机号认证:在Firebase控制台→Authentication→登录方法中开启手机号登录
- 创建Firestore数据库:用于存储用户的手机号、加密密码和关联UID,需设置安全规则确保数据访问权限
二、前端React实现步骤
1. 初始化Firebase
安装依赖:
npm install firebase bcryptjs
(bcryptjs用于密码哈希加密,禁止明文存储)
创建Firebase配置文件:
// firebaseConfig.js import { initializeApp } from "firebase/app"; import { getAuth } from "firebase/auth"; import { getFirestore } from "firebase/firestore"; const firebaseConfig = { apiKey: "YOUR_API_KEY", authDomain: "YOUR_AUTH_DOMAIN", projectId: "YOUR_PROJECT_ID", storageBucket: "YOUR_STORAGE_BUCKET", messagingSenderId: "YOUR_MESSAGING_SENDER_ID", appId: "YOUR_APP_ID" }; const app = initializeApp(firebaseConfig); export const auth = getAuth(app); export const db = getFirestore(app);
2. 注册组件(OTP验证+密码设置)
import { useState } from "react"; import { auth, db } from "./firebaseConfig"; import { signInWithPhoneNumber, RecaptchaVerifier } from "firebase/auth"; import { doc, setDoc } from "firebase/firestore"; import bcrypt from "bcryptjs"; const Register = () => { const [phoneNumber, setPhoneNumber] = useState(""); const [otp, setOtp] = useState(""); const [password, setPassword] = useState(""); const [confirmationResult, setConfirmationResult] = useState(null); // 初始化reCAPTCHA验证器(Firebase手机号认证强制要求) const setupRecaptcha = () => { window.recaptchaVerifier = new RecaptchaVerifier( "recaptcha-container", { size: "invisible" }, auth ); }; // 发送OTP验证码 const handleSendOtp = async () => { setupRecaptcha(); try { const appVerifier = window.recaptchaVerifier; const response = await signInWithPhoneNumber(auth, phoneNumber, appVerifier); setConfirmationResult(response); alert("OTP已发送至您的手机号"); } catch (error) { console.error("发送OTP失败:", error); alert("发送失败,请检查手机号格式并重试"); } }; // 验证OTP并完成注册 const handleVerifyAndRegister = async () => { if (!confirmationResult) return; try { // 验证OTP,完成Firebase手机号认证 const userCredential = await confirmationResult.confirm(otp); const user = userCredential.user; // 哈希加密密码 const hashedPassword = await bcrypt.hash(password, 10); // 将用户信息存储到Firestore await setDoc(doc(db, "users", user.uid), { phoneNumber: phoneNumber, hashedPassword: hashedPassword, uid: user.uid }); alert("注册成功!"); // 此处可跳转至首页或登录页 } catch (error) { console.error("注册失败:", error); alert("验证失败,请检查验证码或密码格式"); } }; return ( <div className="register-box"> <h2>用户注册</h2> <div id="recaptcha-container"></div> <input type="tel" placeholder="请输入手机号(格式:+86138xxxx1234)" value={phoneNumber} onChange={(e) => setPhoneNumber(e.target.value)} /> <button onClick={handleSendOtp}>发送验证码</button> {confirmationResult && ( <> <input type="text" placeholder="请输入6位验证码" value={otp} onChange={(e) => setOtp(e.target.value)} /> <input type="password" placeholder="设置登录密码" value={password} onChange={(e) => setPassword(e.target.value)} /> <button onClick={handleVerifyAndRegister}>完成注册</button> </> )} </div> ); }; export default Register;
3. 登录组件(手机号+密码验证)
import { useState } from "react"; import { auth, db } from "./firebaseConfig"; import { signInWithPhoneNumber, RecaptchaVerifier } from "firebase/auth"; import { collection, query, where, getDocs } from "firebase/firestore"; import bcrypt from "bcryptjs"; const Login = () => { const [phoneNumber, setPhoneNumber] = useState(""); const [password, setPassword] = useState(""); // 初始化登录用reCAPTCHA const setupRecaptcha = () => { window.recaptchaVerifier = new RecaptchaVerifier( "login-recaptcha", { size: "invisible" }, auth ); }; const handleLogin = async () => { try { // 从Firestore查询该手机号的用户数据 const userQuery = query(collection(db, "users"), where("phoneNumber", "==", phoneNumber)); const querySnapshot = await getDocs(userQuery); if (querySnapshot.empty) { alert("该手机号未注册"); return; } const userData = querySnapshot.docs[0].data(); // 比对密码哈希值 const isPasswordValid = await bcrypt.compare(password, userData.hashedPassword); if (!isPasswordValid) { alert("密码错误"); return; } // 密码验证通过,发送OTP完成Firebase登录 setupRecaptcha(); const appVerifier = window.recaptchaVerifier; const response = await signInWithPhoneNumber(auth, phoneNumber, appVerifier); const otp = prompt("请输入登录验证码"); if (!otp) return; await response.confirm(otp); alert("登录成功!"); // 跳转至首页 } catch (error) { console.error("登录失败:", error); alert("登录失败,请检查账号密码或重试"); } }; return ( <div className="login-box"> <h2>用户登录</h2> <div id="login-recaptcha"></div> <input type="tel" placeholder="请输入手机号" value={phoneNumber} onChange={(e) => setPhoneNumber(e.target.value)} /> <input type="password" placeholder="请输入登录密码" value={password} onChange={(e) => setPassword(e.target.value)} /> <button onClick={handleLogin}>登录</button> </div> ); }; export default Login;
三、安全注意事项
- 密码存储:必须使用bcrypt等哈希算法加密,禁止明文存储密码
- Firestore安全规则:设置规则限制数据访问权限,示例规则:
rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /users/{uid} { allow read, write: if request.auth != null && request.auth.uid == uid; } } }
- reCAPTCHA验证:必须启用,防止机器人恶意攻击
- 密码重置:可通过手机号OTP验证身份后,更新Firestore中的哈希密码
内容的提问来源于stack exchange,提问作者Adarsh Kumar Maurya
相关产品推荐
相关产品推荐

