RxJava+Retrofit2中刷新Access Token后如何重试getdata方法?
处理Access Token过期自动刷新并重试请求的实现方案
这个场景在需要身份验证的应用里太常见了,我来分享一套实战中好用的实现思路,不管是前端浏览器环境还是Node后端都能适配:
核心思路
我们需要封装一个通用请求拦截层,自动处理Token过期的情况:
- 每次请求带上当前的Access Token
- 捕获Token过期的自定义错误
- 触发Refresh Token刷新逻辑(注意避免并发请求重复刷新)
- 更新并保存新的双Token
- 重试原本的请求
代码实现示例(JavaScript)
1. 初始化全局状态管理
先定义几个变量来管理Token状态和请求队列,避免重复刷新:
// 存储当前有效Token(可以从本地存储初始化,比如页面加载时读取localStorage) let currentTokens = { accessToken: localStorage.getItem('accessToken') || '', refreshToken: localStorage.getItem('refreshToken') || '' }; // 标记是否正在刷新Token,防止并发请求重复触发刷新 let isRefreshing = false; // 存储等待刷新完成后重试的请求队列 let requestQueue = [];
2. 封装通用请求函数
所有业务请求都通过这个函数发起,自动处理Token校验:
async function makeRequest(url, options = {}) { // 给请求头添加身份验证信息 const authHeaders = { Authorization: `Bearer ${currentTokens.accessToken}` }; const mergedOptions = { ...options, headers: { ...options.headers, ...authHeaders } }; try { const response = await fetch(url, mergedOptions); const responseData = await response.json(); // 这里根据后端约定的错误标识判断Token过期,比如后端返回code: 10001 if (response.status === 401 && responseData.code === 10001) { throw new Error('ACCESS_TOKEN_EXPIRED'); } return responseData; } catch (error) { // 捕获到Token过期错误,进入刷新重试流程 if (error.message === 'ACCESS_TOKEN_EXPIRED') { return handleTokenRefreshAndRetry(() => makeRequest(url, options)); } // 其他错误直接抛出,交给业务层处理 throw error; } }
3. 实现Token刷新与请求重试逻辑
这个函数是核心,处理刷新Token和队列请求的重试:
async function handleTokenRefreshAndRetry(retryFn) { // 如果正在刷新,把当前请求加入队列等待 if (isRefreshing) { return new Promise((resolve) => { requestQueue.push(() => resolve(retryFn())); }); } isRefreshing = true; try { // 调用后端的refreshToken接口获取新Token const newTokens = await fetch('/api/refresh-token', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ refreshToken: currentTokens.refreshToken }) }).then(res => res.json()); // 更新全局Token并保存到本地 currentTokens = newTokens; localStorage.setItem('accessToken', newTokens.accessToken); localStorage.setItem('refreshToken', newTokens.refreshToken); // 刷新完成后,批量重试队列里的所有请求 requestQueue.forEach(callback => callback()); requestQueue = []; // 重试当前请求 return retryFn(); } catch (refreshError) { // 如果Refresh Token也失效了,直接引导用户重新登录 console.error('Refresh Token已过期,请重新登录', refreshError); // 清空队列,避免后续请求一直等待 requestQueue = []; // 这里可以跳转到登录页,或者抛出错误给业务层处理 throw new Error('请重新登录'); } finally { // 无论刷新成功失败,都要重置刷新状态 isRefreshing = false; } }
4. 业务请求封装(比如你的getdata方法)
业务层直接调用封装好的请求函数,不用关心Token逻辑:
async function getdata() { return makeRequest('/api/get-data'); } // 使用示例 getdata() .then(data => console.log('数据获取成功:', data)) .catch(err => console.error('请求失败:', err));
关键注意事项
- 并发请求处理:通过
isRefreshing锁和requestQueue,避免多个请求同时触发Refresh Token接口,减少无效请求。 - Token存储安全:前端建议把Refresh Token存在HttpOnly Cookie中(防止XSS),Access Token存在内存里;后端可以把Token存在Redis等缓存中,设置对应过期时间。
- 错误边界处理:一定要处理Refresh Token失效的情况,不能让请求无限等待,必须引导用户重新登录。
- 自定义错误匹配:实际项目中要和后端约定好Token过期的错误标识,不要只依赖HTTP 401状态码(因为401可能还有其他场景,比如权限不足)。
内容的提问来源于stack exchange,提问作者JunDev
相关产品推荐
相关产品推荐

