Go Gin+Next.js认证系统中Access Token与Refresh Token处理咨询
Access Token与Refresh Token处理方案(Gin+Next.js)
一、何时触发Token刷新?两种方案结合更稳妥
- 被动触发(401拦截):这是最通用的实现方式,不需要前端维护复杂的定时逻辑。但要注意在后端区分「Access Token过期」和「Token无效/权限不足」的401场景,避免无效的刷新请求。
- 主动触发(定时预刷新):在Access Token即将过期前(比如设置为过期时间的80%节点),主动发起刷新请求。适合对用户体验要求高的场景,避免请求失败后再重试的卡顿感。
建议两种方案结合:前端优先做预刷新兜底,若预刷新失败(比如网络问题),再通过401拦截逻辑重试。
二、基于数据库存储Refresh Token的刷新流程
后端(Gin)实现步骤
- 定义刷新接口:接口仅接收Refresh Token(建议通过
HttpOnly+Secure的Cookie传递),返回新的Access Token。 - 验证Refresh Token合法性:
- 从请求中取出Refresh Token,查询数据库对应的记录(需关联用户ID、过期时间、是否被吊销)。
- 若数据库中存在该Token且未过期、未被吊销,生成新的短有效期Access Token(比如15分钟)。
- 可选:生成新的Refresh Token,替换数据库中的旧记录(滚动刷新,提升安全性)。
- 返回新Token:将新的Access Token存入Cookie,返回成功响应。
示例代码(Gin):
import ( "net/http" "time" "github.com/gin-gonic/gin" "your-project/models" "your-project/utils" ) // 刷新Token接口 func RefreshTokenHandler(c *gin.Context) { // 从Cookie获取Refresh Token refreshToken, err := c.Cookie("refresh_token") if err != nil { c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"msg": "缺少有效Refresh Token"}) return } // 查询数据库中的Refresh Token记录 var tokenRecord models.RefreshToken if err := models.DB.Where("token = ? AND expires_at > ?", refreshToken, time.Now()).First(&tokenRecord).Error; err != nil { c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"msg": "Refresh Token无效或已过期"}) return } // 生成新的Access Token accessToken, err := utils.GenerateAccessToken(tokenRecord.UserID) if err != nil { c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"msg": "生成Access Token失败"}) return } // 滚动刷新Refresh Token(可选) newRefreshToken, err := utils.GenerateRefreshToken(tokenRecord.UserID) if err != nil { c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"msg": "生成Refresh Token失败"}) return } // 更新数据库记录 tokenRecord.Token = newRefreshToken tokenRecord.ExpiresAt = time.Now().Add(7 * 24 * time.Hour) models.DB.Save(&tokenRecord) // 设置新的Access Token到Cookie c.SetCookie("access_token", accessToken, 15*60, "/", "your-domain.com", true, true) // 若滚动刷新,同步更新Refresh Token的Cookie c.SetCookie("refresh_token", newRefreshToken, 7*24*3600, "/", "your-domain.com", true, true) c.JSON(http.StatusOK, gin.H{"msg": "Token刷新成功"}) }
前端(Next.js)实现步骤
- 封装请求拦截器:用
axios或Next.js的fetch封装请求,每次请求自动携带Access Token(从Cookie中读取)。 - 响应拦截器处理401:捕获401响应,判断为Access Token过期后,调用刷新接口,成功后重试原请求。
- 定时预刷新逻辑:页面加载时解析Access Token的过期时间,设置定时器在过期前5分钟发起刷新请求。
示例代码(Next.js + axios):
import axios from 'axios'; import { useEffect } from 'react'; const api = axios.create({ baseURL: '/api', withCredentials: true // 允许携带Cookie }); // 响应拦截器 api.interceptors.response.use( res => res, async err => { const originalReq = err.config; // 判断是否为Access Token过期的401,且未重试过 if (err.response.status === 401 && !originalReq._retry) { originalReq._retry = true; try { // 调用刷新接口 await api.post('/refresh-token'); // 重试原请求 return api(originalReq); } catch (refreshErr) { // 刷新失败,跳转到登录页 window.location.href = '/login'; return Promise.reject(refreshErr); } } return Promise.reject(err); } ); // 定时预刷新函数 const setupRefreshTimer = () => { const accessToken = document.cookie.split('; ').find(row => row.startsWith('access_token='))?.split('=')[1]; if (!accessToken) return; // 解析JWT的过期时间(假设Access Token为JWT格式) const payload = JSON.parse(atob(accessToken.split('.')[1])); const expiresAt = payload.exp * 1000; // 提前5分钟触发刷新 const refreshDelay = expiresAt - 5 * 60 * 1000 - Date.now(); if (refreshDelay > 0) { setTimeout(async () => { try { await api.post('/refresh-token'); // 刷新成功后重置定时器 setupRefreshTimer(); } catch (err) { console.error('预刷新Token失败:', err); } }, refreshDelay); } }; // 页面加载时启动定时器 useEffect(() => { setupRefreshTimer(); }, []); export default api;
三、解决过期Access Token的安全顾虑
你担心的「过期Access Token泄露后被用来刷新」是不存在的——正确的刷新流程根本不依赖Access Token,只认数据库中存储的Refresh Token。前端在Access Token过期后,是用Refresh Token去请求新的Access Token,而非过期的Access Token。
核心安全措施:
- Refresh Token必须存储在数据库,同时标记过期时间、吊销状态(用户登出时立即吊销对应记录)。
- Refresh Token的Cookie必须设置
HttpOnly、Secure、SameSite=Strict,防止XSS和CSRF攻击。 - 启用滚动刷新:每次刷新Access Token时生成新的Refresh Token替换旧的,即使旧Token泄露也会失效。
- Access Token使用短有效期(15-30分钟),缩小泄露后的风险窗口。
四、额外优化建议
- 不要将Refresh Token存在前端localStorage,必须用
HttpOnlyCookie,避免XSS窃取。 - 限制用户有效Refresh Token数量:每个用户最多保留1-2个有效Token(比如仅允许当前设备登录),降低多设备登录的安全风险。
- 给Refresh Token设置合理有效期(比如7天),到期后强制用户重新登录。
内容的提问来源于stack exchange,提问作者Bilal Nizamani
相关产品推荐
相关产品推荐

