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

React前端JWT刷新accessToken返回Promise问题修复

问题根因

你拿到Promise而非实际token字符串,核心是调用异步的refresh()函数时没有加await等待执行结果,直接把未决议的Promise对象存到了sessionStorage里。除此之外你的代码还有几个隐藏问题:

  • 文件初始化时一次性读取storage里的token,后续token更新后不会同步读取最新值
  • 刷新拿到新token后没有更新axios实例的请求头,重试请求还是带旧token
  • 没有并发锁,多个请求同时过期时会重复触发token刷新
  • POST请求传参用了body字段,axios识别不到,后端收不到请求参数
  • 没有做网络错误的兜底判断,断网场景下读error.response.status会直接报错

修复步骤

1. 修正前端axios封装逻辑

替换frontend/src/api/axios.js的全部代码,解决上述所有问题:

import axios from "axios";

const BASE_URL = "http://localhost:5000";

export default axios.create({
  baseURL: BASE_URL,
  headers: { "Content-Type": "application/json" },
});

const client = axios.create({ baseURL: BASE_URL });
// 刷新token的并发锁,避免多个请求同时过期时重复调用刷新接口
let isRefreshing = false;
let failedQueue = [];

const processQueue = (error, token = null) => {
  failedQueue.forEach(prom => {
    if (error) {
      prom.reject(error);
    } else {
      prom.resolve(token);
    }
  })
  failedQueue = [];
}

export const axiosAuth = async ({ ...options }) => {
  // 每次请求实时读取storage里的最新token,不要在文件初始化时一次性读取
  const accessToken = sessionStorage.getItem("accessToken");
  client.defaults.headers.common.Authorization = `Bearer ${accessToken}`;

  const onSuccess = (response) => response;
  const onError = async (error) => {
    const originalRequest = error.config;
    // 先判断响应存在,避免网络错误时读取status报错
    if (error.response?.status === 403 && !originalRequest._retry) {
      if (isRefreshing) {
        // 刷新过程中到来的请求先加入等待队列
        return new Promise(function(resolve, reject) {
          failedQueue.push({resolve, reject})
        }).then(token => {
          originalRequest.headers['Authorization'] = 'Bearer ' + token;
          return client(originalRequest);
        }).catch(err => {
          return Promise.reject(err);
        })
      }

      originalRequest._retry = true;
      isRefreshing = true;
      const refreshToken = localStorage.getItem("refreshToken");

      try {
        // 加await等待刷新请求返回,拿到实际的token字符串
        const { data } = await axios.post(`${BASE_URL}/auth/refresh/token`, {
          token: refreshToken,
        });
        const newAccessToken = data.accessToken;
        // 新token存入storage
        sessionStorage.setItem("accessToken", newAccessToken);
        // 更新axios默认请求头
        client.defaults.headers.common.Authorization = `Bearer ${newAccessToken}`;
        // 执行队列里等待的请求
        processQueue(null, newAccessToken);
        // 重试最开始触发403的原请求
        return client(originalRequest);
      } catch (refreshError) {
        processQueue(refreshError, null);
        // 刷新失败(一般是refreshToken过期/无效),清除本地登录态跳登录页
        sessionStorage.removeItem("accessToken");
        localStorage.removeItem("refreshToken");
        window.location.href = "/login";
        return Promise.reject(refreshError);
      } finally {
        isRefreshing = false;
      }
    }

    return Promise.reject(error);
  };

  return client(options).then(onSuccess).catch(onError);
};

2. 修正业务组件的请求参数

frontend/src/modals/Tweet.modal.js里发POST请求时,axios的请求体字段是data不是body,原写法后端收不到推文内容,把调用部分修改为:

await axiosAuth({
  url: "/posts/create",
  method: "POST",
  data: { textfield }, // 把原来的body字段改为data
});

3. (可选优化)后端状态码规范调整

当前鉴权中间件不管是token过期、还是token伪造无效都返回403,建议调整backend/middlewares/authenticateToken.js,把token过期场景单独返回401,和无权限的403做区分,方便前端精准判断是否需要刷新token:

const jwt = require("jsonwebtoken");
require("dotenv").config();

module.exports = function authenticateToken(req, res, next) {
  const authHeader = req.headers["authorization"];
  const token = authHeader && authHeader.split(" ")[1];
  if (token == null) return res.sendStatus(401);

  jwt.verify(token, process.env.ACCESS_TOKEN_SECRET, (err, user) => {
    if (err) {
      // token过期返回401,其他无效/篡改场景返回403
      if (err.name === "TokenExpiredError") return res.sendStatus(401);
      return res.sendStatus(403);
    }
    req.user = user;
    next();
  });
};

调整后把前端axios拦截里的状态码判断从error.response?.status === 403改成error.response?.status === 401即可,逻辑会更严谨。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 19:42:22