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

PromiseKit:实现认证API 401时刷新令牌并重试的方案求助

解决方案:带自动令牌刷新的API请求封装

我来给你提供一个实用的实现方案,以JavaScript为例,核心是通过Promise机制封装请求逻辑,自动处理401状态下的令牌刷新与请求重试,同时避免并发场景下的重复刷新问题:

核心实现思路

  • 封装带认证的基础请求函数,判断响应状态码
  • 捕获401未授权状态,触发令牌刷新流程
  • 刷新成功后更新令牌,并重试原API请求
  • 处理刷新失败的边界情况(比如刷新接口也返回401)
  • 加入并发控制,防止多个请求同时触发重复刷新

代码示例

// 全局变量:保存正在进行的刷新请求,避免重复调用刷新接口
let ongoingRefreshPromise = null;

// 模拟从本地存储获取当前访问令牌(实际项目可替换为状态管理库读取)
const getCurrentAccessToken = () => localStorage.getItem('accessToken');

// 模拟保存新的访问令牌
const saveNewAccessToken = (newToken) => localStorage.setItem('accessToken', newToken);

// 刷新令牌的API请求函数
const refreshAuthToken = async () => {
  // 如果已有正在进行的刷新请求,直接返回这个Promise
  if (ongoingRefreshPromise) {
    return ongoingRefreshPromise;
  }

  try {
    ongoingRefreshPromise = fetch('/api/refresh-token', {
      method: 'POST',
      // 根据你的接口设计携带刷新凭证,比如refresh token或cookie
      credentials: 'include'
    }).then(async (res) => {
      if (!res.ok) throw new Error('令牌刷新失败');
      const { accessToken } = await res.json();
      saveNewAccessToken(accessToken);
      return accessToken;
    }).finally(() => {
      // 刷新完成后清空全局Promise,允许下次触发刷新
      ongoingRefreshPromise = null;
    });

    return ongoingRefreshPromise;
  } catch (err) {
    // 刷新失败时清除本地无效令牌,触发登录逻辑
    localStorage.removeItem('accessToken');
    throw err;
  }
};

// 带自动重试的认证请求封装
const authRequest = async (url, options = {}) => {
  const token = getCurrentAccessToken();
  const requestOptions = {
    ...options,
    headers: {
      ...options.headers,
      Authorization: `Bearer ${token}` // 根据你的认证方式调整,比如Basic Auth
    }
  };

  const response = await fetch(url, requestOptions);

  // 状态码200-299,直接返回响应数据
  if (response.ok) {
    return response.json();
  }

  // 处理401未授权情况
  if (response.status === 401) {
    try {
      // 刷新令牌
      await refreshAuthToken();
      // 刷新成功后,递归调用原请求重试
      return authRequest(url, options);
    } catch (refreshErr) {
      throw new Error('令牌已失效且刷新失败,请重新登录');
    }
  }

  // 其他错误状态,直接抛出错误
  throw new Error(`请求失败,状态码:${response.status}`);
};

关键细节说明

  • 并发刷新控制:通过ongoingRefreshPromise全局变量,确保多个请求同时触发401时,只会发起一次令牌刷新,避免接口重复调用。
  • 令牌管理:getCurrentAccessToken和saveNewAccessToken是抽象的令牌读写函数,你可以根据项目技术栈(比如Vuex、Redux、SessionStorage)替换实现。
  • 错误边界处理:刷新令牌失败时,主动清除本地无效令牌并抛出错误,方便上层逻辑(比如全局错误拦截器)处理登出或跳转登录页的操作。
  • 重试逻辑:刷新成功后递归调用authRequest,复用原请求的所有参数,保证重试请求的准确性。

如果你的项目使用Axios等请求库,逻辑是类似的——可以通过请求拦截器注入令牌,响应拦截器捕获401并触发刷新重试,核心逻辑和上面的实现一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 07:14:34