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

React应用中Axios拦截器刷新Token引发无限循环问题排查与修复

Fixing Axios Interceptor Infinite Loop with Concurrent Requests

Hey, I've run into this exact issue before—concurrent requests all trying to refresh the token at once creates a total mess. Let's break down why this happens and how to fix it:

The Root Problem

When multiple requests fire at the same time and all detect the access token is expired, every single one triggers a refresh token request. This leads to duplicate refresh calls, race conditions when updating localStorage, and eventually that infinite loop you're seeing.

The Solution: Add a Refresh Lock & Request Queue

We need to ensure only one refresh request runs at a time, and queue up other requests until the new token is available. Here's how to adjust your code:

import axios from 'axios'
import jwt_decode from "jwt-decode";
import dayjs from 'dayjs'

const baseURL = 'http://127.0.0.1:8000'
let authTokens = localStorage.getItem('authTokens') ? JSON.parse(localStorage.getItem('authTokens')) : null;
// Add these two variables to track refresh state and queue pending requests
let isRefreshing = false;
let requestsQueue = [];

const axiosInstance = axios.create({
  baseURL,
  headers: { Authorization: `Bearer ${authTokens?.access}` }
});

axiosInstance.interceptors.request.use(async req => {
  if (!authTokens) {
    authTokens = localStorage.getItem('authTokens') ? JSON.parse(localStorage.getItem('authTokens')) : null;
    req.headers.Authorization = `Bearer ${authTokens?.access}`;
  }

  const user = jwt_decode(authTokens.access);
  const isExpired = dayjs.unix(user.exp).diff(dayjs()) < 1;

  // If token isn't expired, just proceed with the request
  if (!isExpired) return req;

  // Case 1: We're already refreshing the token—add this request to the queue
  if (isRefreshing) {
    return new Promise((resolve) => {
      requestsQueue.push(() => {
        req.headers.Authorization = `Bearer ${authTokens.access}`;
        resolve(axiosInstance(req));
      });
    });
  }

  // Case 2: Start the refresh process
  isRefreshing = true;
  try {
    const response = await axios.post(`${baseURL}/api/token/refresh/`, {
      refresh: authTokens.refresh
    });

    // Update tokens in localStorage and our in-memory variable
    localStorage.setItem('authTokens', JSON.stringify(response.data));
    authTokens = response.data;

    // Update the current request's header with the new token
    req.headers.Authorization = `Bearer ${response.data.access}`;

    // Process all queued requests with the fresh token
    requestsQueue.forEach(callback => callback());
    requestsQueue = [];
    return req;
  } catch (error) {
    // Handle refresh failure (e.g., refresh token expired)
    localStorage.removeItem('authTokens');
    authTokens = null;
    // Redirect to login or handle error based on your app's flow
    window.location.href = '/login';
    return Promise.reject(error);
  } finally {
    // Reset the refresh lock no matter if refresh succeeded or failed
    isRefreshing = false;
  }
});

export default axiosInstance;

Key Changes Explained

  • isRefreshing flag: Acts as a lock to prevent multiple concurrent refresh requests. Once a refresh starts, other requests wait instead of firing their own refresh calls.
  • requestsQueue: Stores requests that came in while a refresh was in progress. After getting the new token, we replay all these requests with the updated authorization header.
  • Promise wrapping for queued requests: Ensures pending requests don't fail immediately—they wait until the new token is ready, then retry automatically.
  • Error handling: If the refresh token is expired (or any other error occurs), we clean up localStorage and redirect to login, which stops the loop in its tracks.

This should eliminate the infinite loop and make your concurrent requests behave correctly!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 17:57:50