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
相关产品推荐
相关产品推荐

