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

JavaScript中多AJAX请求并发时的Token刷新问题及解决方案咨询

JavaScript中多AJAX请求并发时的Token刷新问题及解决方案咨询

嗨,这个问题其实在多请求并发的场景里挺常见的,核心矛盾就是多个请求同时触发Token刷新,导致旧的Refresh Token被第一个刷新请求作废后,后面的请求拿着失效的Token去刷新就失败了。下面给你几个能直接落地的前端解决方案,都是实战里常用的:

方案一:用“刷新锁”+请求队列控制

这是最通用的解决思路,核心就是给Token刷新加个“锁”,同一时间只允许一个刷新请求,其他需要刷新的请求先排队等,等新Token拿到后再统一重发。

具体步骤可以这么做:

  • 先定义两个全局变量:isRefreshing(布尔值,标记当前是不是正在刷新Token)、requestQueue(数组,用来存等待重发的请求)。
  • 在你的$.ajaxPrefilter里,当检测到请求返回401时:
    • 如果isRefreshing是false,就把它设为true,立刻发起Token刷新请求。
    • 如果isRefreshing已经是true,就把当前这个请求的重试逻辑放进队列里,先暂停执行。
  • 等Token刷新成功后:
    • 更新本地存储的accessToken和refreshToken。
    • 遍历队列里的所有请求,用新Token重新发起这些请求,然后清空队列。
    • 把isRefreshing改回false,解锁。
  • 如果刷新失败(比如refreshToken也过期了),直接清空队列跳登录页就行。

给你写个jQuery版本的示例代码,你可以参考着改:

// 全局状态变量,用来控制刷新锁和请求队列
let isRefreshing = false;
let requestQueue = [];

$.ajaxPrefilter(function(options, originalOptions, jqXHR) {
  // 给每个请求带上当前的accessToken
  options.headers = options.headers || {};
  options.headers['Authorization'] = 'Bearer ' + localStorage.getItem('accessToken');

  // 处理401未授权错误
  jqXHR.fail(function(xhr) {
    if (xhr.status === 401) {
      // 封装原请求的重试逻辑
      const retryOriginalRequest = () => $.ajax(originalOptions);

      if (!isRefreshing) {
        isRefreshing = true;
        // 发起Token刷新请求
        $.ajax({
          url: '/api/refresh-token',
          method: 'POST',
          data: { refreshToken: localStorage.getItem('refreshToken') }
        }).done(function(res) {
          // 更新本地的Token
          localStorage.setItem('accessToken', res.accessToken);
          localStorage.setItem('refreshToken', res.refreshToken);
          // 重发队列里所有等待的请求
          requestQueue.forEach(callback => callback());
          requestQueue = [];
        }).fail(function() {
          // 刷新失败,跳转到登录页
          window.location.href = '/login';
        }).always(function() {
          // 不管成功失败,都解锁
          isRefreshing = false;
        });
      }

      // 将当前请求的重试逻辑加入队列,返回Promise让请求等待
      return new Promise(resolve => {
        requestQueue.push(() => {
          resolve(retryOriginalRequest());
        });
      });
    }
  });
});

方案二:提前预判Token过期,主动刷新

如果你的accessToken是JWT格式,你可以提前解析它的过期时间,在Token快过期的时候(比如提前10秒)主动发起一次刷新,而不是等401错误出现再处理。这样能大幅减少并发冲突的概率,不过还是要配合方案一的锁机制,防止多次主动刷新。

举个解析JWT过期时间的小例子:

// 解析JWT的过期时间(转成毫秒)
function getTokenExpiryTime(token) {
  if (!token) return 0;
  // JWT的payload是中间那段,用base64解码
  const payload = JSON.parse(atob(token.split('.')[1]));
  return payload.exp * 1000;
}

// 页面加载或每次请求前检查Token是否快过期
const currentToken = localStorage.getItem('accessToken');
const expiryTime = getTokenExpiryTime(currentToken);
const now = Date.now();
// 提前10秒刷新,同时确保没有正在进行的刷新请求
if (expiryTime - now < 10000 && !isRefreshing) {
  // 这里调用方案一里的刷新逻辑就行
}

方案三:后端配合调整Refresh Token逻辑

如果前端改起来觉得麻烦,也可以和后端同事商量,给Refresh Token加个“宽限期”——比如刷新后,旧的Refresh Token不会立刻失效,而是保留30秒的有效期。这样即使后续请求拿着旧Token去刷新,在宽限期内也能成功。不过这个方案需要后端修改逻辑,而且要权衡安全性,宽限期不能太长。

总的来说,方案一是最稳妥的前端独立解决方案,能从根本上解决并发请求的冲突问题,建议你优先试试。如果能结合方案二的主动刷新,用户体验会更好。

备注:内容来源于stack exchange,提问作者Kryten

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.14 14:29:35