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

React.js中LocalStorage管理认证中间件:token无法立即存储问题排查

问题描述

调用认证API后,将返回的token存入LocalStorage,随后跳转至需要token验证的仪表盘页面,但跳转后仍被拦截回登录页,token无法立即生效。

原因分析
  • 路由规则一次性固化:App组件中的useEffect依赖为空数组,仅在组件首次挂载时执行一次,生成所有路由规则。此时LocalStorage中还没有token,所以需要认证的路由(如仪表盘)对应的element被固定设置为跳转登录页的<Navigate>组件。后续登录存入token后,路由规则不会重新生成,跳转时依然使用旧规则,导致被拦截。
  • 验证逻辑非动态:路由的认证判断是在初始化时完成的,并非每次路由渲染时动态检查LocalStorage的token状态,因此token更新后无法触发验证逻辑的更新。
解决方案建议

方案1:使用私有路由组件(推荐)

创建一个专门的私有路由组件,每次渲染时动态检查token状态,替代一次性的路由规则判断:

  1. 新建PrivateRoute.js组件:
import { Navigate } from 'react-router-dom';

export const PrivateRoute = ({ children }) => {
  const hasToken = localStorage.getItem('token');
  return hasToken ? children : <Navigate to='/auth/login' state={{ from: window.location.pathname }} />;
};
  1. 修改App.js中的路由生成逻辑:
// 导入PrivateRoute
import { PrivateRoute } from './components/PrivateRoute';

// ...其他代码

useEffect(() => {
  const getMenu = routes.map((route, index) => {
    return (route.component) ? (
      <Route 
        key={index}
        path={route.path}
        exact={route.exact}
        name={route.name}
        element={route.auth ? <PrivateRoute>{route.component}</PrivateRoute> : route.component}
      />
    ) : null;
  });
  setMenu(getMenu);
  setMount(true);
}, []);

这样每次访问需要认证的路由时,都会实时检查LocalStorage中的token状态,登录后跳转即可正常访问。

方案2:监听token变化更新路由

在App组件中监听LocalStorage的变化,当token更新时重新生成路由规则:

useEffect(() => {
  const updateRouteMenu = () => {
    const newMenu = routes.map((route, index) => {
      return (route.component) ? (
        <Route 
          key={index}
          path={route.path}
          exact={route.exact}
          name={route.name}
          element={route.auth ? localStorage.getItem('token') ? route.component : 
          <Navigate to='/auth/login' state={{ from: route.path }} /> : route.component}
        />
      ) : null;
    });
    setMenu(newMenu);
  };

  // 初始化生成路由
  updateRouteMenu();
  setMount(true);

  // 监听LocalStorage变化(跨标签页)
  window.addEventListener('storage', updateRouteMenu);

  // 当前标签页修改token后手动触发更新
  const handleTokenUpdate = () => updateRouteMenu();
  window.addEventListener('tokenUpdate', handleTokenUpdate);

  return () => {
    window.removeEventListener('storage', updateRouteMenu);
    window.removeEventListener('tokenUpdate', handleTokenUpdate);
  };
}, []);

然后在登录提交函数中,存入token后手动触发更新:

const handleSubmit = async(event) => {
    event.preventDefault();
    const data = new FormData(event.currentTarget);
    const loginData = {
      email: data.get('email'),
      password: data.get('password'),
    };
    const user = await login(loginData);
    if (user.token) {
      localStorage.setItem('token', user.token);
      // 手动触发路由更新
      window.dispatchEvent(new Event('tokenUpdate'));
      navigate('/');
    } else {
      toast(user);
    }
 };

方案3:用状态管理存储token

将token存入React Context(或其他状态管理工具),利用状态变化触发组件重新渲染,自动更新路由规则:

  1. 完善AuthContext:
// context/AuthContext.js
import { createContext, useState, useEffect } from 'react';

export const AuthContext = createContext();

export const AuthProvider = ({ children }) => {
  // 从LocalStorage初始化token状态
  const [token, setToken] = useState(localStorage.getItem('token'));

  // 状态同步到LocalStorage
  useEffect(() => {
    if (token) {
      localStorage.setItem('token', token);
    } else {
      localStorage.removeItem('token');
    }
  }, [token]);

  // 登录方法
  const login = (newToken) => {
    setToken(newToken);
  };

  return (
    <AuthContext.Provider value={{ token, login }}>
      {children}
    </AuthContext.Provider>
  );
};
  1. 在登录组件中使用Context:
import { useContext } from 'react';
import { AuthContext } from '../context/AuthContext';

// ...其他代码

const { login } = useContext(AuthContext);

const handleSubmit = async(event) => {
    event.preventDefault();
    const data = new FormData(event.currentTarget);
    const loginData = {
      email: data.get('email'),
      password: data.get('password'),
    };
    const user = await login(loginData);
    if (user.token) {
      login(user.token);
      navigate('/');
    } else {
      toast(user);
    }
 };
  1. 修改App.js中的路由逻辑:
const { token } = useContext(AuthContext);

useEffect(() => {
  const getMenu = routes.map((route, index) => {
    return (route.component) ? (
      <Route 
        key={index}
        path={route.path}
        exact={route.exact}
        name={route.name}
        element={route.auth ? token ? route.component : 
        <Navigate to='/auth/login' state={{ from: route.path }} /> : route.component}
      />
    ) : null;
  });
  setMenu(getMenu);
  setMount(true);
}, [token]); // 依赖token状态,变化时重新生成路由

这样token状态更新时,App组件会重新渲染并生成新的路由规则,跳转后即可正常访问仪表盘。

内容的提问来源于stack exchange,提问作者Christian Rainer Sassmann

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 20:32:56