欢迎您访问新疆栾骏商贸有限公司,公司主营电子五金轴承产品批发业务!
全国咨询热线: 400-8878-609

新闻资讯

技术学院

如何在 JWT Token 过期时自动跳转至登录页

作者:心靈之曲2026-01-01 00:00:00

本文介绍一种基于 useeffect 和 settimeout 的可靠方案,用于监听 jwt token 过期时间,并在过期瞬间触发登出与路由跳转,避免手动轮询或错误的时间比较逻辑。

在 React 应用中,仅依赖客户端时间判断 JWT 是否过期(如在 中直接调用 jwt-decode)存在多个严重问题:

  • 同步解码空/无效 token:sessionStorage.getItem("token") 可能为 null 或已过期,直接传入 jwt_decode() 会抛出异常;
  • 时间格式误用:toLocaleString('en-sg') 返回的是带格式的字符串(如 "12/4/2025, 3:22:15 PM"),不可用于时间大小比较,导致逻辑永远失效;
  • 逻辑冗余且未生效:ProtectedRoutes 中 isAuthActive 函数被定义但从未调用,且返回 JSX 的方式与 React Router v6 的 element 模式不兼容;
  • 无实时响应机制:Token 过期后页面仍可继续访问,直到用户刷新或手动触发保护逻辑。

✅ 正确做法是:将 Token 过期监听提升至应用顶层(如 App.js),利用 useEffect + setTimeout 实现“精准倒计时登出” —— 即根据 exp 时间戳计算剩余毫秒数,设置一次性定时器,在到期时自动 dispatch logout 并导航至 /auth/login。

✅ 推荐实现(App.js 中)

// App.jsx
import { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { useDispatch, useSelector } from 'react-redux';
import jwt_decode from 'jwt-decode';
import { logout } from './features/auth/authSlice';

function App() {
  const navigate = useNavigate();
  const dispatch = useDispatch();
  // 假设 token 存储在 auth.user.token 中(请按实际 state 结构调整)
  const token = useSelector((state) => state.auth.user?.token);

  useEffect(() => {
    if (!token) return;

    let timerRef = null;

    try {
      const decoded = jwt_decode(token);
      const expiryMs = decoded.exp * 1000; // exp 是秒级时间戳
      const nowMs = Date.now();
      const timeout = expiryMs - nowMs;

      const onExpire = () => {
        dispatch(logout()); // 触发异步登出请求(可选)
        navigate('/auth/login', { replace: true });
      };

      if (timeout > 0) {
        timerRef = setTimeout(onExpire, timeout);
      } else {
        // Token 已过期,立即登出跳转
        onExpire();
      }
    } catch (error) {
      console.warn('Invalid or missing JWT token:', error);
      dispatch(logout());
      navigate('/auth/login', { replace: true });
    }

    // 清理定时器(组件卸载或 token 变更时)
    return () => {
      if (timerRef) clearTimeout(timerRef);
    };
  }, [dispatch, navigate, token]);

  return (
    
      {/* 你的 Router 配置,例如  */}
    
  );
}

export default App;

⚠️ 关键注意事项

  • Token 存储位置需一致:确保 useSelector 获取的 token 与 authService.login() 中存入 sessionStorage 的值同步(建议统一由 Redux 管理 token,而非混合使用 sessionStorage);
  • logout() 的服务端调用:当前 authService.logout() 发送登出请求,应确保该接口真正使服务端 Token 失效(如加入黑名单),而不仅是清除本地存储;
  • Token 刷新(Refresh Token)进阶方案:若业务支持自动续期,应在 timeout 触发前 5 分钟发起刷新请求,成功则重置定时器,失败再跳转登录;
  • 兜底防护:即使定时器因页面休眠等失效,也应在每个受保护路由入口(如 ProtectedRoutes)做二次校验(仅检查 exp > Date.now(),不依赖字符串比较):
// ProtectedRoutes.jsx(精简安全版)
import { Navigate, Outlet } from 'react-router-dom';
import jwt_decode from 'jwt-decode';

export default function ProtectedRoutes() {
  const token = sessionStorage.getItem('token');

  if (!token) return ;

  try {
    const { exp } = jwt_decode(token);
    if (Date.now() >= exp * 1000) {
      sessionStorage.removeItem('token');
      return ;
    }
  } catch {
    sessionStorage.removeItem('token');
    return ;
  }

  return ;
}

通过顶层定时器 + 路由守卫双保险,即可实现平滑、可靠、用户体验友好的 Token 过期自动跳转机制。