Token自动续期实现方案详解
Token自动续期是提升用户体验和保障系统安全的关键机制,其核心在于无感刷新和安全可控。以下从原理、实现方案、安全措施和最佳实践四个维度展开说明:
一、核心原理:双Token机制
Token自动续期通常采用 Access Token(访问令牌) + Refresh Token(刷新令牌) 的双Token方案:
- Access Token:短期有效(如15分钟),用于直接访问受保护资源。
- Refresh Token:长期有效(如30天),仅用于获取新的Access Token。
当用户请求资源时,服务端验证Access Token有效性:
- 有效:直接响应请求。
- 过期:客户端使用Refresh Token申请新Access Token,无需用户重新登录。
二、实现方案详解
1. 后端实现流程
(1)登录时生成双Token
// 生成Access Token(15分钟有效期)
String accessToken = JwtUtil.sign(username, secret, 15 * 60 * 1000);
// 生成Refresh Token(30天有效期)
String refreshToken = JwtUtil.sign(username, secret, 30 * 24 * 60 * 60 * 1000);
// 存储到Redis(Access Token过期时间设为双倍,用于续期判断)
redisUtil.set("access_token:" + userId, accessToken, 30 * 60); // 30分钟
redisUtil.set("refresh_token:" + userId, refreshToken, 30 * 24 * 60 * 60); // 30天
(2)请求拦截校验与续期
public boolean preHandle(HttpServletRequest request) {
String token = request.getHeader("Authorization");
// 1. 检查Access Token是否存在
if (token == null) return false;
// 2. 验证Token有效性
try {
JwtUtil.verify(token, secret);
} catch (TokenExpiredException e) {
// 3. Token过期但仍在Redis中,触发续期
String cachedToken = redisUtil.get("access_token:" + userId);
if (cachedToken != null && cachedToken.equals(token)) {
String newToken = JwtUtil.sign(username, secret, 15 * 60 * 1000);
redisUtil.set("access_token:" + userId, newToken, 30 * 60);
request.setAttribute("new_token", newToken); // 返回新Token给前端
} else {
// Redis中Token已失效,需重新登录
return false;
}
}
return true;
}
2. 前端实现流程
(1)请求拦截器逻辑
axios.interceptors.response.use(response => {
// 检查Token是否即将过期(例如剩余1分钟)
const token = localStorage.getItem('access_token');
const decoded = jwtDecode(token);
const expireTime = decoded.exp * 1000;
const now = Date.now();
if (expireTime - now < 60 * 1000) {
// 自动调用刷新接口
return axios.post('/refresh_token', { refresh_token: localStorage.getItem('refresh_token') })
.then(res => {
// 更新本地Token并重试原请求
localStorage.setItem('access_token', res.data.new_token);
return axios(request.config);
});
}
return response;
}, error => {
// 处理Token失效或刷新失败
if (error.response.status === 401) {
redirectToLogin();
}
return Promise.reject(error);
});
(2)刷新Token接口调用
async function refreshToken() {
try {
const res = await axios.post('/refresh_token', {
refresh_token: localStorage.getItem('refresh_token')
});
localStorage.setItem('access_token', res.data.new_token);
return true;
} catch (error) {
logout(); // 刷新失败则退出登录
return false;
}
}
三、安全措施
- 传输安全:全程使用HTTPS防止Token被截获。
- 存储安全:
- 前端:Refresh Token存储在HttpOnly Cookie中,避免XSS攻击。
- 后端:Token加密存储,Redis设置访问权限。
- 生命周期管理:
- Access Token:15分钟有效期,Redis缓存30分钟。
- Refresh Token:30天有效期,支持续期次数限制(如最多刷新50次)。
- 黑名单机制:用户注销或修改密码时,将Refresh Token加入黑名单。
四、最佳实践
- 续期触发策略:
- 时间阈值:Token剩余1分钟时触发刷新。
- 操作感知:用户持续操作(如每5分钟有请求)自动续期。
- 兜底逻辑:
- 强制重新登录:超过72小时未操作,即使Refresh Token有效也需重新认证。
- 刷新次数限制:防止Refresh Token被无限续期。
- 监控与告警:
- 监控Token刷新频率,异常高频触发告警。
- 记录刷新日志,便于审计。
五、方案优势
维度 | 传统单Token方案 | 双Token自动续期方案 |
---|---|---|
用户体验 | 频繁登录打断操作 | 无感刷新,操作连贯 |
安全性 | 长期有效Token风险高 | 短期Access Token降低泄露影响 |
服务端压力 | 每次请求需解密验证 | 续期操作减少重复认证 |
总结:Token自动续期通过双Token机制、缓存校验和前后端协作,实现了安全与体验的平衡。实际部署时需结合业务场景调整Token有效期和续期策略,并严格遵循安全规范。