You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

页面刷新后重定向至登录页问题求助(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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 06:27:11