Firebase Auth页面刷新重定向问题:优化AuthRoute逻辑
解决React + Firebase Auth刷新受保护路由时的重定向问题
在React项目中使用Firebase Auth,通过Context Provider共享登录用户信息,并用AuthRoute组件保护特定路由——未登录用户访问时会被重定向到登录页。但存在一个问题:用户登录后访问受保护路由,刷新页面时会被错误重定向到登录页,此时可观察到Firebase Auth调用了https://identitytoolkit.googleapis.com/v1/accounts:lookup接口。
已配置browserLocalPersistence,原本以为是Firebase Auth未优先读取本地存储,而是直接请求认证接口。进一步调研后发现:即使本地存储存在认证信息,Firebase Auth仍会先与服务器异步校验身份,这就是刷新时AuthRoute组件误以为用户未登录的核心原因。
需要修改AuthRoute组件逻辑,满足以下要求:
- 若本地存储无认证信息,直接重定向至登录页;
- 若本地存储存在认证信息,等待
onAuthStateChanged完成后,根据结果决定渲染受保护内容或重定向至登录页。
原有代码
Firebase初始化代码
const firebaseConfig = { apiKey: "...", authDomain: "...", projectId: "...", storageBucket: "...", messagingSenderId: "...", appId: "...", }; const firebaseApp = initializeApp(firebaseConfig); setPersistence(getAuth(), browserLocalPersistence).then(() => console.log("Set persistence: " + Date.now()) ); export const firebaseAuth = getAuth();
AuthContext Provider核心代码
useEffect(() => { const unsubscribe = onAuthStateChanged(firebaseAuth, (currentUser) => { setUser(currentUser); }); return unsubscribe; }, []); const values = { user, login, signup, logout }; return ( <AuthContext.Provider value={values}> {children} </AuthContext.Provider> );
AuthRoute组件代码
export const AuthRoute: React.FC<Props> = (props) => { const { children } = props; const location = useLocation(); const { user } = useUserAuth(); return user ? ( <> {children} </> ) : ( <Navigate to="/users/login" state={{ returnUrl: location }} /> ); };
修改方案
1. 更新AuthContext,添加加载状态
在AuthContext中新增加载状态,用于标识Firebase Auth的服务器校验是否完成:
const [user, setUser] = useState<User | null>(null); const [isAuthLoading, setIsAuthLoading] = useState(true); // 新增加载状态 useEffect(() => { const unsubscribe = onAuthStateChanged(firebaseAuth, (currentUser) => { setUser(currentUser); setIsAuthLoading(false); // 校验完成后更新状态 }); return unsubscribe; }, []); const values = { user, isAuthLoading, login, signup, logout }; return ( <AuthContext.Provider value={values}> {children} </AuthContext.Provider> );
2. 修改AuthRoute组件逻辑
在AuthRoute中先检查本地存储的Firebase认证信息,再结合加载状态和用户信息做判断:
import { firebaseConfig } from './path-to-firebase-config'; // 导入firebase配置 export const AuthRoute: React.FC<Props> = (props) => { const { children } = props; const location = useLocation(); const { user, isAuthLoading } = useUserAuth(); // 检查本地存储是否存在Firebase Auth的认证数据 const authStorageKey = `firebase:authUser:${firebaseConfig.apiKey}:${firebaseConfig.authDomain}`; const hasLocalAuthData = Boolean(localStorage.getItem(authStorageKey)); // 本地无认证数据,直接重定向到登录页 if (!hasLocalAuthData) { return <Navigate to="/users/login" state={{ returnUrl: location }} />; } // 本地有认证数据,但校验还在进行中,显示加载状态(可替换为项目自定义加载组件) if (isAuthLoading) { return <div>加载中...</div>; } // 校验完成,根据用户状态决定渲染内容或重定向 return user ? ( <> {children} </> ) : ( <Navigate to="/users/login" state={{ returnUrl: location }} /> ); };
3. 优化Firebase初始化(可选)
调整初始化顺序,确保持久化配置在Auth实例创建后正确执行:
const firebaseConfig = { apiKey: "...", authDomain: "...", projectId: "...", storageBucket: "...", messagingSenderId: "...", appId: "...", }; const firebaseApp = initializeApp(firebaseConfig); const firebaseAuth = getAuth(firebaseApp); // 直接关联app实例 setPersistence(firebaseAuth, browserLocalPersistence) .then(() => { console.log("Set persistence: " + Date.now()); }) .catch((error) => { console.error("设置持久化失败:", error); }); export { firebaseAuth };
内容的提问来源于stack exchange,提问作者Andy T
相关产品推荐
相关产品推荐

