封装 axios 拦截器实现用户无感刷新 access_token

为了解决多个请求同时发起时刷新 `access_token` 的问题,需在代码中实现以下关键逻辑: --- ### **1. 优化 `requests` 数组的处理逻辑** 当多个请求同时发起时,需确保所有待重发的请求在 `access_token` 刷新后被正确执行。 ```javascript let isRefreshing = false; let requests = []; // 存储待重发请求的数组 ``` **关键逻辑:** - 在 `refreshToken` 成功后,遍历 `requests` 数组,逐个执行回调函数。 - 刷新完成后清空数组,避免重复处理。 ```javascript instance.interceptors.response.use( response => response, error => { if (!error.response) return Promise.reject(error); if (error.response.status === 401 && !error.config.url.includes('/auth/refresh')) { const { config } = error; if (!isRefreshing) { isRefreshing = true; return refreshToken().then(res => { const { access_token } = res.data; setToken(access_token); config.headers.Authorization = `Bearer ${access_token}`; // 执行所有待重发请求 requests.forEach(cb => cb(access_token)); requests = []; return instance(config); }).catch(err => Promise.reject(err)).finally(() => { isRefreshing = false; }); } else { // 保存未执行 resolve 的 Promise return new Promise(resolve => { requests.push(token => { config.headers.Authorization = `Bearer ${token}`; resolve(instance(config)); }); }); } } return Promise.reject(error); } ); ``` --- ### **2. 确保所有请求在刷新后被正确执行** 在 `refreshToken` 成功后,需确保所有待重发的请求回调函数被正确执行: ```javascript requests.forEach(cb => cb(access_token)); requests = []; ``` **说明:** - `requests` 数组中的每个元素是回调函数(如 `cb(access_token)`),在刷新后逐个执行。 - 刷新完成后清空数组,避免重复处理。 --- ### **3. 处理刷新失败时的未执行 Promise** 当 `refreshToken` 失败时,需将未执行 `resolve` 的 Promise 保存到 `requests` 数组中,以便后续处理: ```javascript return new Promise(resolve => { requests.push(token => { config.headers.Authorization = `Bearer ${token}`; resolve(instance(config)); }); }); ``` **关键点:** - `resolve` 函数被保存到 `requests` 数组中,待 `access_token` 更新后执行。 - 当刷新成功时,数组中的所有回调函数被调用,确保所有请求重发。 --- ### **4. 防止多次刷新 `access_token`** 通过 `isRefreshing` 标志控制刷新状态,避免在刷新期间同时发起新请求: ```javascript if (!isRefreshing) { isRefreshing = true; return refreshToken().then(...); } ``` **说明:** - 如果正在刷新 `access_token`,新请求直接返回未执行 `resolve` 的 Promise,避免重复处理。 --- ### **最终优化后的代码** ```javascript // request.js import axios from 'axios'; import { getToken, setToken, getRefreshToken } from './auth'; const instance = axios.create({ baseURL: process.env.GATSBY_API_URL, timeout: 30000, headers: { 'Content-Type': 'application/json' } }); let isRefreshing = false; let requests = []; instance.interceptors.response.use( response => response, error => { if (!error.response) return Promise.reject(error); if (error.response.status === 401 && !error.config.url.includes('/auth/refresh')) { const { config } = error; if (!isRefreshing) { isRefreshing = true; return refreshToken().then(res => { const { access_token } = res.data; setToken(access_token); config.headers.Authorization = `Bearer ${access_token}`; requests.forEach(cb => cb(access_token)); requests = []; return instance(config); }).catch(err => Promise.reject(err)).finally(() => { isRefreshing = false; }); } else { return new Promise(resolve => { requests.push(token => { config.headers.Authorization = `Bearer ${token}`; resolve(instance(config)); }); }); } } return Promise.reject(error); } ); const setHeaderToken = (isNeedToken) => { const accessToken = isNeedToken ? getToken() : null; if (isNeedToken) { instance.defaults.headers.common.Authorization = `Bearer ${accessToken}`; } }; export const get = (url, params = {}, isNeedToken = false) => { setHeaderToken(isNeedToken); return instance({ method: 'get', url, params }); }; export const post = (url, data, isNeedToken = false) => { setHeaderToken(isNeedToken); return instance({ method: 'post', url, data }); }; ``` --- ### **总结** - **关键逻辑**:通过 `requests` 数组保存待重发请求,确保所有请求在 `access_token` 更新后被正确执行。 - **防止重复刷新**:使用 `isRefreshing` 标志控制刷新状态,避免在刷新期间发起新请求。 - **处理失败情况**:将未执行 `resolve` 的 Promise 保存到数组中,确保后续请求被正确触发。 此方案可有效解决多请求同时发起时刷新 `access_token` 的问题,确保所有请求在令牌更新后被正确处理。

发表评论 (审核通过后显示评论):

昵称:
邮箱:
内容: