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

求助:JWT令牌过期时自动跳转至登录页的实现方案

JWT令牌过期自动跳转登录页的实现方案与代码修复

现有代码的核心问题

  1. ProtectedRoutes.js未处理token不存在的情况,直接调用jwt_decode会抛出错误;isAuthActive函数定义后未被使用,且用字符串格式的时间比较(toLocaleString)不可靠;当前仅判断token是否存在,未校验过期状态。
  2. 未结合后端接口的401状态码做全局处理,token过期后接口返回错误时无法自动跳转。
  3. Redux状态与sessionStorage的用户信息不同步,存在状态不一致问题。

一、修复ProtectedRoutes的认证校验逻辑

修改路由守卫,添加容错处理,用时间戳直接比较判断token是否过期:

import { Navigate, Outlet } from "react-router-dom";
import jwt_decode from "jwt-decode";

export default function ProtectedRoutes() {
  const checkTokenValidity = () => {
    const token = sessionStorage.getItem("token");
    if (!token) return false;

    try {
      const decoded = jwt_decode(token);
      // 用时间戳数值比较,避免字符串格式差异导致的错误
      const currentTimestamp = Date.now() / 1000;
      if (decoded.exp < currentTimestamp) {
        sessionStorage.removeItem("token");
        sessionStorage.removeItem("user");
        return false;
      }
      return true;
    } catch (error) {
      // 解码失败(如token篡改),清除无效数据
      sessionStorage.removeItem("token");
      sessionStorage.removeItem("user");
      return false;
    }
  };

  const isAuthenticated = checkTokenValidity();

  return isAuthenticated ? <Outlet /> : <Navigate to="/auth/login" replace />;
}

二、添加登录后的自动过期定时器(可选)

在登录成功后设置8小时定时器,到期自动执行登出,和后端token有效期对齐:
修改authSlice.js的login.fulfilled逻辑:

.addCase(login.fulfilled, (state, action) => {
  state.isSuccess = true;
  state.user = action.payload;
  sessionStorage.setItem("user", JSON.stringify(action.payload));
  
  // 设置8小时后自动登出
  const expiryTime = 8 * 60 * 60 * 1000; // 8小时转换为毫秒
  setTimeout(() => {
    logout();
    window.location.href = "/auth/login";
  }, expiryTime);
})

注意:页面刷新后定时器会失效,因此路由守卫的校验仍是核心逻辑,定时器仅作为补充。


三、全局请求拦截器处理后端401状态

添加axios拦截器,统一处理token过期后接口返回的401错误:
修改authService.js:

import axios from "axios";

// 创建axios实例
const api = axios.create({
  baseURL: import.meta.env.VITE_BACKEND_STAGING_URL
});

// 请求拦截器:自动携带token到请求头
api.interceptors.request.use((config) => {
  const token = sessionStorage.getItem("token");
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});

// 响应拦截器:捕获401错误,清除数据并跳转登录页
api.interceptors.response.use(
  (response) => response,
  (error) => {
    if (error.response?.status === 401) {
      sessionStorage.removeItem("token");
      sessionStorage.removeItem("user");
      window.location.href = "/auth/login";
    }
    return Promise.reject(error);
  }
);

// 修改登录、登出函数使用api实例
const login = async (formData) => {
  const response = await api.post("/api/v1/auth/signin", formData);
  if (response.data) {
    const { token } = response.data;
    sessionStorage.setItem("token", token);
  }
  return response.data;
};

const logout = async () => {
  const response = await api.post("/api/v1/auth/logout");
  if (response.data) {
    sessionStorage.removeItem("token");
    sessionStorage.removeItem("user");
  }
  return response.data;
};

export { login, logout, api };

四、同步Redux状态与sessionStorage

修改authSlice.js的初始状态和登出逻辑,确保状态与本地存储同步:

const initialState = {
  // 从sessionStorage读取用户信息,保持与token存储位置一致
  user: sessionStorage.getItem("token") ? JSON.parse(sessionStorage.getItem("user")) : null,
  isLoading: false,
  isError: false,
  isSuccess: false,
};

// 修改登出逻辑
.addCase(logout.fulfilled, (state) => {
  state.isSuccess = true;
  state.user = null;
  sessionStorage.removeItem("user");
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 00:54:52