无感刷新Token

无感刷新Token

孤独的哈士奇 0 2026-03-25

现在我们在面对Token过期的时候 经常采用401的方式让用户重新去登录 这样的方式合理 但不够完美

如果能让用户毫无感知的重新登录 用户体验绝对会更好

对此 我专门找后端研究了一下这个方案 并且最后成功在一个项目中实现 下面是基本流程:

HTTP/1.1 200 OK
Set-Cookie: refreshToken=abc123; HttpOnly; Secure; SameSite=Strict; Path=/auth

第二步:前端实现“单例刷新锁 + 请求队列”

let isRefreshing = false;
let refreshPromise = null;
const failedQueue = [];

// 重试队列中的请求
const processQueue = (error, token = null) => {
  failedQueue.forEach(({ resolve, reject }) => {
    if (error) {
      reject(error);
    } else {
      resolve(token);
    }
  });
  failedQueue.length = 0;
};

axios.interceptors.response.use(
  response => response,
  async (error) => {
    const originalRequest = error.config;

    if (error.response?.status === 401 && !originalRequest._retry) {
      if (isRefreshing) {
        // 已在刷新中,将请求加入队列,等待新 token
        return new Promise((resolve, reject) => {
          failedQueue.push({ resolve, reject });
        }).then(token => {
          originalRequest.headers['Authorization'] = `Bearer ${token}`;
          return axios(originalRequest);
        });
      }

      originalRequest._retry = true;
      isRefreshing = true;

      try {
        // 调用刷新接口(后端从 Cookie 读 refreshToken)
        const { data } = await axios.post('/auth/refresh');
        const newAccessToken = data.accessToken;

        // 通知所有排队的请求
        processQueue(null, newAccessToken);

        // 重试当前请求
        originalRequest.headers['Authorization'] = `Bearer ${newAccessToken}`;
        return axios(originalRequest);
      } catch (refreshError) {
        // 刷新失败:清空本地身份,跳转登录
        clearAuth();
        processQueue(refreshError, null);
        window.location.href = '/login';
        return Promise.reject(refreshError);
      } finally {
        isRefreshing = false;
        refreshPromise = null;
      }
    }

    return Promise.reject(error);
  }
);

最后进行测试:

  1. 手动将 Access Token 设为过期;

  2. 快速点击多个按钮,触发并发请求;

  3. 观察 Network 面板:

    • 是否只调用了一次 /auth/refresh

    • 所有原请求是否最终成功?

  4. 模拟 Refresh Token 失效,是否跳转登录?

完美实现!!