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

