5分钟搞定前后端无感刷新:accessToken与refreshToken实战指南(含axios拦截器配置)
5分钟搞定前后端无感刷新accessToken与refreshToken实战指南含axios拦截器配置在当今的Web应用开发中用户认证是一个绕不开的话题。传统的单token方案虽然简单但当token过期时强制用户重新登录的体验实在称不上优雅。想象一下用户正在填写一个复杂的表单突然因为token过期而被踢出系统所有输入的内容都丢失了——这种体验足以让用户对产品产生负面印象。无感刷新机制正是为了解决这一问题而生。通过accessToken和refreshToken的巧妙配合我们可以在用户毫无感知的情况下完成token的更新保持会话的连续性。本文将聚焦于中小型项目的快速实现方案提供一套即插即用的解决方案让你在5分钟内就能为项目添加这一重要功能。1. 理解双token机制的核心原理在开始编码之前我们需要先理解accessToken和refreshToken这对搭档是如何协同工作的。accessToken就像是一张短期通行证有效期通常较短如15分钟到2小时用于日常的API请求认证。而refreshToken则是一张长期通行证有效期可达数天甚至数周专门用于在accessToken过期时获取新的accessToken。这种设计有几个关键优势安全性即使accessToken被截获攻击者也只能在短时间内滥用用户体验避免了频繁要求用户重新登录的困扰灵活性可以随时调整refreshToken的有效期或撤销特定设备的refreshToken提示虽然refreshToken有效期较长但并不意味着可以无限期使用。合理的做法是设置一个适中的有效期如7天并在用户主动退出时立即撤销所有refreshToken。2. 前端实现axios拦截器配置axios的拦截器功能为我们实现无感刷新提供了完美的工具。下面是一个完整的实现方案// 创建axios实例 const service axios.create({ baseURL: process.env.VUE_APP_BASE_API, timeout: 5000 }) // 请求拦截器 service.interceptors.request.use( config { const accessToken localStorage.getItem(accessToken) if (accessToken) { config.headers[Authorization] Bearer ${accessToken} } return config }, error { return Promise.reject(error) } ) // 响应拦截器 service.interceptors.response.use( response { return response.data }, async error { const originalRequest error.config // 检查是否是token过期错误 if (error.response.status 401 !originalRequest._retry) { originalRequest._retry true try { // 尝试刷新token const refreshToken localStorage.getItem(refreshToken) const res await axios.post(/auth/refresh, { refreshToken }) // 存储新的accessToken localStorage.setItem(accessToken, res.data.accessToken) // 重试原始请求 originalRequest.headers[Authorization] Bearer ${res.data.accessToken} return service(originalRequest) } catch (refreshError) { // 刷新失败跳转到登录页 localStorage.removeItem(accessToken) localStorage.removeItem(refreshToken) router.push(/login) return Promise.reject(refreshError) } } return Promise.reject(error) } )这段代码实现了以下功能在请求头中自动添加accessToken检测401错误token过期自动使用refreshToken获取新的accessToken重试原始请求处理刷新失败的情况3. 后端实现简洁高效的token管理后端需要提供两个关键接口登录接口和刷新接口。以下是使用Node.js和Express的实现示例const jwt require(jsonwebtoken) const redis require(redis) // 配置 const config { accessTokenSecret: your_access_secret, refreshTokenSecret: your_refresh_secret, accessTokenExpiry: 15m, refreshTokenExpiry: 7d } // Redis客户端 const redisClient redis.createClient() redisClient.on(error, (err) console.log(Redis Client Error, err)) // 登录接口 app.post(/login, (req, res) { const { username, password } req.body // 验证用户凭据简化示例 if (username ! admin || password ! password) { return res.status(401).json({ error: Invalid credentials }) } // 生成token const accessToken jwt.sign({ username }, config.accessTokenSecret, { expiresIn: config.accessTokenExpiry }) const refreshToken jwt.sign({ username }, config.refreshTokenSecret, { expiresIn: config.refreshTokenExpiry }) // 存储refreshToken到Redis redisClient.set(refreshToken, username) redisClient.expire(refreshToken, 60 * 60 * 24 * 7) // 7天 res.json({ accessToken, refreshToken }) }) // 刷新接口 app.post(/auth/refresh, (req, res) { const { refreshToken } req.body // 验证refreshToken jwt.verify(refreshToken, config.refreshTokenSecret, (err, decoded) { if (err) { return res.status(401).json({ error: Invalid refresh token }) } // 检查Redis中是否存在该refreshToken redisClient.get(refreshToken, (err, reply) { if (!reply) { return res.status(401).json({ error: Refresh token expired }) } // 生成新的accessToken const newAccessToken jwt.sign( { username: decoded.username }, config.accessTokenSecret, { expiresIn: config.accessTokenExpiry } ) res.json({ accessToken: newAccessToken }) }) }) })这个实现包含了几个关键点使用不同的密钥和有效期生成accessToken和refreshToken将refreshToken存储在Redis中以便验证和管理提供清晰的错误响应4. 常见问题与解决方案在实际应用中你可能会遇到以下问题4.1 并发请求导致的多次刷新当多个请求同时收到401错误时可能会触发多次刷新请求。解决方案是let isRefreshing false let failedQueue [] const processQueue (error, token null) { failedQueue.forEach(prom { if (error) { prom.reject(error) } else { prom.resolve(token) } }) failedQueue [] } service.interceptors.response.use( response response, async error { const originalRequest error.config if (error.response.status 401 !originalRequest._retry) { if (isRefreshing) { return new Promise((resolve, reject) { failedQueue.push({ resolve, reject }) }).then(token { originalRequest.headers[Authorization] Bearer token return service(originalRequest) }).catch(err { return Promise.reject(err) }) } originalRequest._retry true isRefreshing true try { const refreshToken localStorage.getItem(refreshToken) const res await axios.post(/auth/refresh, { refreshToken }) localStorage.setItem(accessToken, res.data.accessToken) processQueue(null, res.data.accessToken) originalRequest.headers[Authorization] Bearer res.data.accessToken return service(originalRequest) } catch (error) { processQueue(error, null) localStorage.removeItem(accessToken) localStorage.removeItem(refreshToken) router.push(/login) return Promise.reject(error) } finally { isRefreshing false } } return Promise.reject(error) } )4.2 安全性最佳实践为了确保token机制的安全性建议遵循以下原则安全措施实施方法作用HTTPS全站启用HTTPS防止token在传输中被窃取HttpOnly Cookie将refreshToken存储在HttpOnly Cookie中防止XSS攻击窃取token短期有效期accessToken设置较短有效期15-120分钟减少token泄露后的风险窗口密钥轮换定期更换签名密钥降低密钥泄露的影响黑名单维护已撤销但未过期的token列表及时阻止被撤销的token4.3 性能优化对于高并发应用可以考虑以下优化批量验证当需要验证多个token时使用Redis的批量操作命令本地缓存在内存中缓存有效的token减少Redis查询连接池使用Redis连接池提高连接复用率5. 测试与调试技巧确保你的无感刷新机制正常工作至关重要。以下是一些测试建议手动测试流程登录获取token等待accessToken过期或手动修改使其过期发起API请求观察是否自动刷新检查新token是否被正确存储和使用自动化测试脚本describe(Token Refresh, () { let accessToken, refreshToken before(async () { // 登录获取初始token const res await request(app) .post(/login) .send({ username: test, password: test }) accessToken res.body.accessToken refreshToken res.body.refreshToken }) it(should refresh token when expired, async () { // 模拟token过期 const expiredToken jwt.sign( { username: test }, your_access_secret, { expiresIn: -1s } ) // 设置过期的token localStorage.setItem(accessToken, expiredToken) // 发起请求 const res await service.get(/protected) // 验证 expect(res.status).toBe(200) expect(localStorage.getItem(accessToken)).not.toBe(expiredToken) }) })调试工具使用Chrome开发者工具查看网络请求在拦截器中添加console.log调试关键步骤使用Redis CLI监控token存储情况在实际项目中我发现设置合理的token过期时间非常重要。accessToken太短会导致频繁刷新太长则安全性降低。经过多次测试15-30分钟的accessToken配合7天的refreshToken在大多数场景下都能取得良好的平衡。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2437865.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!