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

React中JWT过期未自动跳转登录页问题及解决请求

问题分析

你的代码存在三个核心问题导致JWT过期后无法自动跳转,仅能通过刷新页面触发:

  • useEffect仅监听auth变量变化,当checkLogin修改isLoggedIn状态后,不会触发useEffect执行跳转逻辑
  • checkLogin中修改isLoggedIn是异步状态更新,useEffect里直接判断isLoggedIn拿到的是初始值,无法正确触发跳转
  • 缺少定时检查token过期的机制,页面打开期间token过期时不会主动检测
解决方案

步骤1:统一登出逻辑

抽离handleLogout函数,集中处理状态更新、清除token、跳转等操作,避免重复代码。

步骤2:添加定时过期检测

在checkLogin中计算token剩余有效期,设置定时器在token过期时自动触发登出,确保页面打开期间token过期能及时响应。

步骤3:完善副作用依赖与清理

组件挂载时执行一次登录检查,同时添加清理函数在组件卸载时清除定时器,防止内存泄漏。

修改后的完整代码
import React, { useEffect, useState } from 'react';
import { Navigate, useRoutes, useNavigate } from 'react-router-dom';
import JwtDecode from "jwt-decode";
// layouts
import DashboardLayout from './layouts/dashboard';
import SimpleLayout from './layouts/simple';

// pages
import SignIn from './pages/Auth/SignIn';
import SignUp from './pages/Auth/SignUp';
import Otp from './pages/Auth/Otp';
import Dashboard from './pages/Dashboard/AppAnalytics';
import AlumniListing from './pages/Alumni/AlumniListing';
import Page404 from './pages/Page404';
import Profile from './pages/Profile/index';
import EditProfile from './pages/EditProfile/index';
import Payment from './pages/Auth/Payment';
import PaymentStatus from './pages/Auth/PaymentStatus';

export default function Router() {
  const [isLoggedIn, setIsLoggedIn] = useState(false);
  const navigate = useNavigate();
  const [expirationTimer, setExpirationTimer] = useState(null);

  const handleLogout = () => {
    setIsLoggedIn(false);
    localStorage.removeItem("token");
    if (expirationTimer) clearTimeout(expirationTimer);
    navigate("/auth/sign-in", { replace: true });
  };

  const checkLogin = () => {
    const auth = localStorage.getItem("token");
    if (auth) {
      try {
        const decodedToken = JwtDecode(auth);
        const currentTime = Math.floor(new Date().getTime() / 1000);
        
        if (currentTime >= decodedToken.exp) {
          console.log("JWT已过期");
          handleLogout();
        } else {
          console.log("JWT有效");
          setIsLoggedIn(true);
          // 设置定时器,在token过期时自动登出
          const timeUntilExpire = (decodedToken.exp - currentTime) * 1000;
          const timer = setTimeout(handleLogout, timeUntilExpire);
          setExpirationTimer(timer);
        }
      } catch (error) {
        console.error("解码JWT出错:", error);
        handleLogout();
      }
    } else {
      setIsLoggedIn(false);
    }
  };

  useEffect(() => {
    checkLogin();
    // 组件卸载时清理定时器
    return () => {
      if (expirationTimer) clearTimeout(expirationTimer);
    };
  }, []);

  const routes = useRoutes([
    {
      path: '/dashboard',
      element: isLoggedIn ? <DashboardLayout /> : <Navigate to='/auth/sign-in' replace />,
      children: [
        { element: <Navigate to="/dashboard/analytics" replace />, index: true },
        { path: 'analytics', element: <Dashboard /> },
        { path: 'alumni', element: <AlumniListing /> },
        { path: 'profile', element: <Profile /> },
        { path: 'edit-profile', element: <EditProfile /> },
      ],
    },
    {
      path: 'auth/sign-in',
      element: !isLoggedIn ? <SignIn /> : <Navigate to='/dashboard/analytics' replace />,
    },
    {
      path: 'payment',
      element: <Payment />,
    },
    {
      path: 'payment/gateway-response',
      element: <PaymentStatus />,
    },
    {
      path: 'verify-otp',
      element: !isLoggedIn ? <Otp /> : <Navigate to='/dashboard/analytics' replace />,
    },
    {
      path: 'auth/sign-up',
      element: !isLoggedIn ? <SignUp /> : <Navigate to='/dashboard/analytics' replace />,
    },
    {
      element: <SimpleLayout />,
      children: [
        { path: '404', element: <Page404 /> },
        { path: '*', element: <Navigate to="/404" replace /> },
      ],
    },
    {
      path: '*',
      element: <Navigate to="/404" replace />,
    },
  ]);

  return routes;
}
关键修改点说明
  • 新增handleLogout函数统一处理登出流程,包含状态更新、清除token、清理定时器和跳转操作
  • 在checkLogin中计算token剩余有效期,设置定时器自动触发登出,实现页面打开期间的过期检测
  • useEffect仅在组件挂载时执行一次登录检查,添加清理函数避免定时器内存泄漏
  • 路由跳转使用replace属性,防止历史记录中堆积无效跳转记录

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 12:51:17