React应用中如何安全地使用认证信息进行路由控制?
解决思路&方案
1. 别自己存UID到state,用Firebase Auth的原生状态监听
你现在靠本地state存UID判断登录状态,确实容易被篡改。直接用Firebase Auth提供的onAuthStateChanged监听用户状态,这个状态由Firebase维护,客户端没法篡改。替换原来的逻辑:
import { onAuthStateChanged } from "firebase/auth"; import { auth } from "./firebase-config"; const Login = () => { const [loginError, setLoginError] = useState(); const navigate = useNavigate(); useEffect(() => { const unsubscribe = onAuthStateChanged(auth, (user) => { if (user) { // 登录成功直接跳转,不用存UID到state navigate("/dashboard"); } }); // 组件卸载时取消监听,避免内存泄漏 return () => unsubscribe(); }, [navigate]); const loginUserHandler = (email, password) => { signInWithEmailAndPassword(auth, email, password) .catch((error) => { setLoginError(fbAuthErrorCodeToString(error.code)); }); }; // ... 其他组件内容 };
2. 给受保护路由加全局守卫
不管用户怎么折腾客户端状态,路由层面必须做真实校验。封装一个ProtectedRoute组件,每次进入路由前都检查Firebase的真实登录状态:
import { Navigate, Outlet } from "react-router-dom"; import { onAuthStateChanged } from "firebase/auth"; import { auth } from "./firebase-config"; import { useState, useEffect } from "react"; const ProtectedRoute = () => { const [isLoggedIn, setIsLoggedIn] = useState(null); useEffect(() => { const unsubscribe = onAuthStateChanged(auth, (user) => { setIsLoggedIn(!!user); }); return () => unsubscribe(); }, []); // 加载中显示加载组件,避免白屏 if (isLoggedIn === null) { return <div>加载中...</div>; } // 已登录就放行,未登录踢回登录页 return isLoggedIn ? <Outlet /> : <Navigate to="/login" />; };
然后在路由配置里用这个组件包裹所有需要保护的路由:
import { Routes, Route } from "react-router-dom"; function App() { return ( <Routes> <Route path="/login" element={<Login />} /> {/* 所有受保护路由都放在这个容器下 */} <Route element={<ProtectedRoute />}> <Route path="/dashboard" element={<Dashboard />} /> {/* 其他需要权限的路由 */} </Route> {/* 无效路由直接跳登录页 */} <Route path="*" element={<Navigate to="/login" />} /> </Routes> ); }
3. Firestore安全规则必须兜底
就算用户侥幸进入了路由,Firestore这边必须把好最后一关,确保只有对应UID的用户能访问自己的数据。配置这样的安全规则:
rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { // 仅允许登录用户访问自己的用户文档 match /users/{userId} { allow read, write: if request.auth != null && request.auth.uid == userId; } // 若有Dashboard相关集合,同理校验UID match /dashboard-content/{userId} { allow read: if request.auth != null && request.auth.uid == userId; } } }
额外小技巧:别给未登录用户暴露路由信息
比如导航栏,只有用户登录后才显示Dashboard这类链接,避免用户猜测路由URI,从源头减少不必要的麻烦。
内容的提问来源于stack exchange,提问作者Bromox
相关产品推荐
相关产品推荐

