页面刷新后重定向至登录页问题求助(React+Express)
解决页面刷新后重定向至登录页的问题
核心原因
当前认证状态仅保存在React组件的内存状态中,页面刷新后组件重新挂载,所有状态被重置为初始的null值,导致路由守卫RequireAuth判定用户未登录,从而触发重定向到登录页。
解决方案
将用户认证信息持久化到浏览器的localStorage(或sessionStorage),在应用初始化时从存储中恢复状态,确保页面刷新后认证状态不丢失。
1. 更新auth.js,添加持久化逻辑
修改AuthProvider,初始化时从localStorage读取用户信息,登录时同步保存到存储,登出时清除存储内容:
import { createContext, useContext, useState, useEffect } from "react"; const AuthContext = createContext(null); export const AuthProvider = ({ children }) => { // 初始化时从localStorage读取已保存的用户信息 const [id, setID] = useState(() => localStorage.getItem('auth_id') || null); const [name, setName] = useState(() => localStorage.getItem('auth_name') || null); const [email, setEmail] = useState(() => localStorage.getItem('auth_email') || null); const [pwd, setPwd] = useState(() => localStorage.getItem('auth_pwd') || null); const [role, setRole] = useState(() => localStorage.getItem('auth_role') || null); // 监听状态变化,自动同步到localStorage useEffect(() => { if (id && email && role) { localStorage.setItem('auth_id', id); localStorage.setItem('auth_name', name); localStorage.setItem('auth_email', email); localStorage.setItem('auth_pwd', pwd); localStorage.setItem('auth_role', role); } }, [id, name, email, pwd, role]); const logIn = (id, name, email, pwd, role) => { setID(id); setName(name); setEmail(email); setPwd(pwd); setRole(role); }; const logOut = () => { setID(null); setName(null); setEmail(null); setPwd(null); setRole(null); // 登出时清除localStorage中的所有认证信息 localStorage.removeItem('auth_id'); localStorage.removeItem('auth_name'); localStorage.removeItem('auth_email'); localStorage.removeItem('auth_pwd'); localStorage.removeItem('auth_role'); }; return ( <AuthContext.Provider value={{ id, name, email, pwd, role, logIn, logOut }}> {children} </AuthContext.Provider> ); }; export const useAuth = () => { return useContext(AuthContext); };
2. 优化RequireAuth的判断逻辑
当前判断条件存在逻辑冗余,只需验证核心标识(如role)是否存在即可判定用户是否已登录:
import { Navigate } from "react-router-dom"; import { useAuth } from "./auth"; export const RequireAuth = ({ children }) => { const auth = useAuth(); // 只要role不存在,就判定为未登录状态 if (!auth.role) { return <Navigate to="/" replace />; } return children; };
3. 可选:提升后端认证安全性(重要)
当前后端直接返回明文密码存在严重安全隐患,建议做以下优化:
- 使用
bcrypt存储用户密码的哈希值,登录时对比哈希而非明文 - 生成JWT令牌返回给前端,前端存储令牌用于后续请求验证,不再存储密码
后端修改示例(基于JWT和bcrypt)
const bcrypt = require('bcrypt'); const jwt = require('jsonwebtoken'); // 创建用户时哈希密码 app.post("/createUser", async (req, res) => { const { name, email, password, role, status } = req.body; // 生成密码哈希 const hashedPwd = await bcrypt.hash(password, 10); const sql = "INSERT INTO users (name, email, password, role, status) VALUES (?, ?, ?, ?, ?)"; const values = [name, email, hashedPwd, role, status]; db.query(sql, values, (err, result) => { if (err) { console.error("Error creating user:", err); return res.status(500).json({ error: "Server error" }); } res.json({ status: "Success" }); }); }); // 登录时验证哈希密码并返回JWT app.post("/login", async (req, res) => { const { email, password } = req.body; db.query("SELECT * FROM users WHERE email = ?", [email], async (error, results) => { if (error) { console.error("Error executing MySQL query:", error); return res.status(500).json({ message: "Internal server error" }); } if (results.length === 0) { return res.status(404).json({ message: "User does not exist" }); } const user = results[0]; if (user.status === "inactive") { return res.status(403).json({ message: "Your account is disabled. Please contact admin for assistance.", }); } // 验证密码哈希是否匹配 const isMatch = await bcrypt.compare(password, user.password); if (!isMatch) { return res.status(401).json({ message: "Invalid email or password" }); } // 生成24小时有效期的JWT令牌 const token = jwt.sign( { id: user.id, email: user.email, role: user.role }, process.env.JWT_SECRET, { expiresIn: '24h' } ); // 返回用户基本信息和令牌(不包含密码) res.json({ id: user.id, username: user.name, email: user.email, role: user.role, token: token }); }); });
前端对应修改(存储JWT而非密码)
在LogIn.js的登录成功回调中,存储JWT到localStorage,并在auth.js中添加令牌验证逻辑:
// LogIn.js的handleLogin修改 auth.logIn( user.id, user.username, user.email, null, // 不再存储密码 user.role ); // 存储JWT令牌 localStorage.setItem('auth_token', user.token); // auth.js中添加JWT验证逻辑 useEffect(() => { const token = localStorage.getItem('auth_token'); if (token) { try { // 前端解码验证令牌有效性(也可调用后端接口验证) const decoded = jwt.verify(token, process.env.REACT_APP_JWT_SECRET); // 恢复用户状态 setID(decoded.id); setEmail(decoded.email); setRole(decoded.role); } catch (err) { // 令牌无效,清除存储 localStorage.removeItem('auth_token'); } } }, []);
内容的提问来源于stack exchange,提问作者Hassaan Uddin Sheikh
相关产品推荐
相关产品推荐

