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

React应用中如何监听JWT token过期并自动跳转至登录页

解决方案

以下几个方案可以独立或组合使用,覆盖所有token过期场景:

1. JWT过期时间预计算+一次性定时器(性能最优,优先选择)

JWT的payload中本身就携带exp过期时间戳字段,你可以在用户登录拿到token时直接解码拿到过期时间,计算当前时间到过期时间的差值,设置一次性定时器,到点自动触发登出逻辑即可。
需要提前安装jwt-decode工具库:npm i jwt-decode
自定义hook示例:

import { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import jwtDecode from 'jwt-decode';

// 全局存储定时器id,方便手动登出时清理
let logoutTimerId = null;

export const useAutoAuthCheck = () => {
  const navigate = useNavigate();

  // 统一登出逻辑
  const handleLogout = () => {
    localStorage.removeItem('token');
    if (logoutTimerId) clearTimeout(logoutTimerId);
    navigate('/login', { replace: true });
  };

  useEffect(() => {
    const token = localStorage.getItem('token');
    // 无token直接跳登录
    if (!token) {
      handleLogout();
      return;
    }

    try {
      const decodedToken = jwtDecode(token);
      // 转换为毫秒级时间戳计算剩余有效期
      const expireTimeMs = decodedToken.exp * 1000;
      const currentTimeMs = Date.now();
      const remainingValidTime = expireTimeMs - currentTimeMs;

      // 已经过期直接登出
      if (remainingValidTime <= 0) {
        handleLogout();
        return;
      }

      // 设置定时器,到期自动登出
      logoutTimerId = setTimeout(handleLogout, remainingValidTime);
    } catch (err) {
      // token解码失败说明是伪造/篡改的token,直接登出
      handleLogout();
    }

    // 组件卸载时清理定时器,避免内存泄漏
    return () => {
      if (logoutTimerId) clearTimeout(logoutTimerId);
    };
  }, [navigate]);

  return { handleLogout };
};

你只需要在应用的根组件(比如App.jsx)中调用一次这个hook,就可以全局生效,不需要依赖页面刷新。

2. 请求响应拦截器统一校验

配合后端接口返回逻辑,所有发往后端的请求都添加响应拦截器,只要后端返回401 Unauthorized状态码(对应token过期、无效、被作废等场景),直接触发登出逻辑,这个方案可以覆盖token被后端提前作废、篡改等JWT本身校验不出来的场景。
Axios拦截器示例:

import axios from 'axios';

const apiClient = axios.create({
  baseURL: '你的后端接口根地址'
});

apiClient.interceptors.response.use(
  (response) => response,
  (error) => {
    if (error.response?.status === 401) {
      localStorage.removeItem('token');
      window.location.replace('/login');
    }
    return Promise.reject(error);
  }
);

export default apiClient;

后续所有接口请求都用这个封装好的apiClient发送即可。

3. 路由守卫跳转校验

给所有需要登录权限的路由添加守卫,每次路由跳转时都校验一次token有效性,只要用户切换页面就会触发校验,不需要等待刷新。
React Router v6 私有路由示例:

import { Navigate, Outlet } from 'react-router-dom';
import jwtDecode from 'jwt-decode';

// 封装token校验方法
const checkTokenValid = () => {
  const token = localStorage.getItem('token');
  if (!token) return false;
  try {
    const decoded = jwtDecode(token);
    return decoded.exp * 1000 > Date.now();
  } catch {
    return false;
  }
};

// 私有路由组件
export const PrivateRoute = () => {
  return checkTokenValid() ? <Outlet /> : <Navigate to="/login" replace />;
};

路由配置使用:

import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import { PrivateRoute } from './PrivateRoute';
import Login from './pages/Login';
import Dashboard from './pages/Dashboard';
import UserCenter from './pages/UserCenter';

const router = createBrowserRouter([
  { path: '/login', element: <Login /> },
  // 所有需要登录权限的路由都放在PrivateRoute下
  {
    element: <PrivateRoute />,
    children: [
      { path: '/dashboard', element: <Dashboard /> },
      { path: '/user', element: <UserCenter /> }
    ]
  }
]);

function App() {
  return <RouterProvider router={router} />
}

最佳实践

  • 推荐将上述三个方案配合使用:预计算定时器保证用户停留在当前页面不刷新、不发请求的场景下到点自动登出,响应拦截器覆盖请求时token失效的场景,路由守卫覆盖页面切换时的校验,三个方案配合可以覆盖所有token过期的场景。
  • 不推荐使用轮询校验的方案,会产生不必要的性能开销,除非有特殊业务需求。

内容的提问来源于stack exchange,提问作者Marlon Englemam

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 10:51:03