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

设置JWT到localStorage后未刷新页面无法携带Token的问题

问题描述

登录后将后端返回的JWT Token保存到localStorage,代码如下:

const login = (event) => {
  event.preventDefault();
  postWithoutAuth("/auth/login", formValues, enqueueSnackbar)
    .then((response) => {
      localStorage.setItem("currentUser", response.data.id);
      localStorage.setItem("username", response.data.username);
      localStorage.setItem("tokenKey", response.data.token);
      localStorage.setItem("roles", JSON.stringify(response.data.roles));
      navigate("/");
      // window.location.reload(true);
    })
};

跳转至首页后,localStorage中的所有值均存在,但点击菜单列表获取数据时,发送给后端的请求未携带JWT值;而如果登录后刷新页面(注释代码所示)则无问题。

需要实现:登录跳转首页后,无需刷新页面,让前端识别localStorage中的值。

列表数据获取方法:

useEffect(() => {
    fetchData();
  }, []);

const fetchData = () => {
  const userId = localStorage.getItem("currentUser");
  getWithAuth("/users/")
    .then((response) => {
      setData(response.data);
    })
};

更新: 获取JWT并传入Axios请求配置的代码:

const AUTH_TOKEN = localStorage.getItem("tokenKey");

const config = {
  headers: {
    "Access-Control-Allow-Origin": "*",
    "Access-Control-Allow-Credentials": true,
    "Content-Type": "application/json",
    Accept: "application/json",
    Authorization: `Bearer ${AUTH_TOKEN}`
  },
};

export const getWithAuth = (url) => {
  const request = axios.get(url, config);
  return request.then((response) => response.data);
};
问题原因

核心问题在于AUTH_TOKEN和config是模块加载时就初始化的,此时用户还未登录,localStorage.getItem("tokenKey")返回null,所以Authorization头是Bearer null。登录后虽然把token存到了localStorage,但这个初始化好的config不会自动更新,后续调用getWithAuth时依然用的是旧的config,导致请求不带有效token。

而刷新页面会重新加载整个模块,此时AUTH_TOKEN会重新从localStorage获取到刚登录存入的token,所以请求正常。

解决方案

方案一:每次请求动态获取token(改动最小)

修改getWithAuth方法,每次发起请求时才去localStorage获取最新的token,动态生成请求配置:

export const getWithAuth = (url) => {
  // 每次请求时实时获取token
  const AUTH_TOKEN = localStorage.getItem("tokenKey");
  const config = {
    headers: {
      "Access-Control-Allow-Origin": "*",
      "Access-Control-Allow-Credentials": true,
      "Content-Type": "application/json",
      Accept: "application/json",
      // 有token才添加Authorization头,避免空值
      ...(AUTH_TOKEN && { Authorization: `Bearer ${AUTH_TOKEN}` })
    },
  };
  const request = axios.get(url, config);
  return request.then((response) => response.data);
};

方案二:使用Axios拦截器(更优雅,全局复用)

创建Axios实例并添加请求拦截器,每次请求前自动注入最新的token,这样所有需要认证的请求都能统一处理:

// 创建Axios实例
const axiosAuthInstance = axios.create({
  baseURL: "/", // 替换为你的API基础路径
  headers: {
    "Access-Control-Allow-Origin": "*",
    "Access-Control-Allow-Credentials": true,
    "Content-Type": "application/json",
    Accept: "application/json",
  },
});

// 添加请求拦截器,每次请求前动态设置token
axiosAuthInstance.interceptors.request.use(
  (config) => {
    const token = localStorage.getItem("tokenKey");
    if (token) {
      config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
  },
  (error) => {
    return Promise.reject(error);
  }
);

// 修改getWithAuth使用这个实例
export const getWithAuth = (url) => {
  return axiosAuthInstance.get(url).then((res) => res.data);
};

两种方案都能解决问题:方案一适合快速修改现有代码,改动范围小;方案二更适合中大型项目,统一管理认证逻辑,后续新增接口无需重复处理token。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 13:29:59