如何利用Refresh Token自动更新JWT Access Token以维持有效访问
基于Quasar的Token静默刷新实现方案
一、最优触发时机选择
结合你的场景,**推荐「提前定时刷新 + 请求前校验兜底」**的组合策略,比单一方式更可靠:
- 定时刷新:在Access Token过期前1-2分钟触发刷新(比如Token有效期10分钟,就在第8分钟发起请求)。这样能保证大部分场景下Token始终有效,避免每次请求都校验的冗余。
- 请求前校验兜底:如果用户长时间闲置(比如15分钟后回到应用),定时任务可能因后台休眠停止,此时在每次API请求前先检查Access Token剩余有效期,若不足30秒,先静默刷新再发起业务请求。
页面切换时刷新的弊端:频繁切换页面会重复触发刷新,增加不必要的API调用,效率不如定时+请求兜底的组合。
二、静默刷新Token的具体实现
1. 核心逻辑封装
在Quasar中封装全局Token管理工具(比如src/utils/token.js),包含以下方法:
// 获取Access Token剩余有效期(秒) export const getTokenRemainingTime = () => { const tokenInfo = JSON.parse(localStorage.getItem('tokenInfo')) || {}; const expiresAt = tokenInfo.expiresAt; // 登录时需存储Token过期时间戳 if (!expiresAt) return 0; return (expiresAt - Date.now()) / 1000; }; // 静默刷新Token export const refreshAccessToken = async () => { const refreshToken = localStorage.getItem('refreshToken'); if (!refreshToken) throw new Error('无可用Refresh Token'); // 调用后端刷新接口 const res = await fetch('/api/auth/refresh', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ refreshToken }) }); const newTokenInfo = await res.json(); // 更新本地存储的Token信息,记录新的过期时间戳 localStorage.setItem('tokenInfo', JSON.stringify({ accessToken: newTokenInfo.accessToken, expiresAt: Date.now() + 10 * 60 * 1000 // 10分钟有效期 })); // 部分后端会返回新的Refresh Token,无则沿用旧的 localStorage.setItem('refreshToken', newTokenInfo.refreshToken || refreshToken); };
2. 定时任务与iOS后台唤醒处理
在src/boot目录下创建token-refresh.js,处理定时任务及iOS后台唤醒场景:
import { getTokenRemainingTime, refreshAccessToken } from 'src/utils/token'; export default async ({ app }) => { let refreshTimer = null; // 启动/重置定时刷新任务 const startRefreshTimer = () => { if (refreshTimer) clearTimeout(refreshTimer); const remainingTime = getTokenRemainingTime(); // 剩余时间大于2分钟则设置在剩余2分钟时刷新,否则立即刷新 const delay = Math.max(remainingTime - 120, 0) * 1000; refreshTimer = setTimeout(async () => { try { await refreshAccessToken(); startRefreshTimer(); // 刷新成功后重置定时任务 } catch (err) { // 刷新失败跳转登录页(比如Refresh Token失效) app.router.push('/login'); } }, delay); }; // 应用启动时初始化定时任务(已登录状态) if (localStorage.getItem('tokenInfo')) { startRefreshTimer(); } // 监听应用前后台切换,解决iOS后台唤醒后定时任务失效问题 document.addEventListener('visibilitychange', () => { if (!document.hidden && localStorage.getItem('tokenInfo')) { const remainingTime = getTokenRemainingTime(); // 剩余时间不足30秒立即刷新,否则重置定时任务 if (remainingTime < 30) { refreshAccessToken().then(startRefreshTimer); } else { startRefreshTimer(); } } }); };
随后在quasar.conf.js的boot数组中添加该文件,确保应用启动时加载。
3. 请求前校验兜底
在API请求拦截器(比如Axios)中添加Token校验逻辑:
import axios from 'axios'; import { getTokenRemainingTime, refreshAccessToken } from 'src/utils/token'; const api = axios.create({ baseURL: '/api' }); api.interceptors.request.use(async (config) => { const tokenInfo = JSON.parse(localStorage.getItem('tokenInfo')) || {}; if (!tokenInfo.accessToken) { window.location.href = '/login'; return Promise.reject(new Error('未登录')); } const remainingTime = getTokenRemainingTime(); // Token剩余有效期不足30秒时,先刷新再发起请求 if (remainingTime < 30) { try { await refreshAccessToken(); const newTokenInfo = JSON.parse(localStorage.getItem('tokenInfo')); config.headers.Authorization = `Bearer ${newTokenInfo.accessToken}`; } catch (err) { window.location.href = '/login'; return Promise.reject(err); } } else { config.headers.Authorization = `Bearer ${tokenInfo.accessToken}`; } return config; }); export default api;
三、关于请求前刷新的频率问题
每次请求前都刷新确实会增加API调用量,但通过有效期阈值判断可避免不必要的刷新:仅当Token剩余时间不足30秒时才触发刷新,其他场景直接使用现有Token发起请求。这样既保证了请求有效性,又不会产生过多冗余的刷新请求。
内容的提问来源于stack exchange,提问作者kappxl
相关产品推荐
相关产品推荐

