刷新凭证应存为HttpOnly、Secure、SameSite=None的引用ID(如UUID),真实refresh token仅存服务端Redis并设TTL,配合HMAC签名防篡改。

Cookie刷新凭证该存到哪里?别放 http.SetCookie 的 value 里
直接把刷新令牌(refresh token)明文塞进 http.SetCookie 的 Value 字段,等于裸奔。攻击者抓个包就能拿到,后续伪造请求毫无压力。正确做法是只存一个短期有效的、不可预测的引用 ID(比如 UUID v4),然后把真实的 refresh token 存在服务端——Redis 最常用,也可用加密数据库字段。
实操建议:
- 生成刷新凭证时:用
uuid.NewString()创建refresh_token_id,同时把真实 refresh token + 用户 ID + 过期时间(如 7 天)以 JSON 或二进制序列化后,写入 Redis,key 为"rt:" + refresh_token_id,并设 TTL - 设置 Cookie 时:
http.SetCookie(w, &http.Cookie{Name: "refresh_token_id", Value: refresh_token_id, HttpOnly: true, Secure: true, SameSite: http.SameSiteStrictMode, MaxAge: 0})</li> <li>别忘了签名:用 <code>gorilla/securecookie或自建 HMAC 签名机制保护refresh_token_idCookie,防篡改
双重验证怎么在 Echo 中链式校验?别在 Handler 里手写 if-else 堆逻辑
Echo 的中间件机制就是干这事的。把「访问令牌校验」和「刷新令牌校验」拆成两个独立中间件,按顺序挂载,失败就直接中断,避免嵌套判断污染业务逻辑。
常见错误现象:在登录成功后只发了 access token,没同步更新 Redis 中对应的 refresh token 记录,导致下次用旧 refresh_token_id 请求时查不到对应值,报 "refresh token not found"。
实操建议:
- access token 校验中间件:解析 JWT,检查
exp、iss、aud,从 payload 提取user_id并注入c.Set("user_id", userID) - refresh token 校验中间件(仅用于
/auth/refresh):读取refresh_token_idCookie → 验证签名 → 查 Redis → 检查是否被撤销(加个"revoked:" + rtID的布尔标记)→ 若通过,再生成新 access token 并更新 Redis 中的 refresh token 记录 - 两者都依赖同一个用户状态缓存层,别各自连 Redis 实例,统一用
redis.Client单例
c.SetCookie() 和 c.Cookie() 在刷新流程中怎么配合?注意 SameSite 和 Secure 的实际影响
很多人以为只要调了 c.SetCookie() 就万事大吉,结果在 Safari 或 Chrome 新版里刷新失败——因为没配对 SameSite 和 Secure。如果前端走 HTTPS,后端却设 Secure: false,浏览器直接丢弃 Cookie;设 SameSite: Lax 又会导致 POST 到 /auth/refresh 时 Cookie 不携带。
实操建议:
- 生产环境必须设
Secure: true,开发用 localhost 时可加条件判断:Secure: c.Request().TLS != nil || c.Request().Host == "localhost:8080" - 刷新接口是 POST,必须设
SameSite: http.SameSiteNoneMode,但这样会强制要求Secure: true,否则浏览器拒绝设置 - 读取时用
c.Cookie("refresh_token_id"),不是c.Request().Cookie()—— 后者不走 Echo 的安全封装,可能绕过 HttpOnly 检查逻辑 - 每次成功刷新后,要主动清除旧的
refresh_token_idCookie(MaxAge=0),再写入新的,防止重放
为什么刷新后还要校验 access token 是否仍在有效期内?避免“双 token 都失效”的静默失败
单纯靠 refresh token 有效期(比如 7 天)不保险。用户可能在 6 天后修改密码,或管理员手动注销设备,这时你光凭 refresh token 还能换出新 access token,就违背了权限即时生效原则。
实操建议:
- 在刷新逻辑里,除了查 Redis 中的 refresh token,还要查用户当前状态:比如读取
"user_status:" + userID的缓存,确认未被禁用、未强制登出 - 给 access token 加一个短生命周期(15–30 分钟),但把用户角色、权限等非敏感字段放进去,减少频繁查库;敏感操作(如转账)仍走实时 DB 校验
- 所有 refresh 接口响应里,必须返回新 access token 的
exp时间戳,前端据此决定是否提前触发刷新,而不是等 401 才动
真正难的不是写完这两个中间件,而是让 refresh token 的生命周期、撤销机制、存储加密、前端自动重试策略全部对齐。漏掉任意一环,就可能出现“看着登录着,点一下就掉线”或者“密码改了还能继续用旧 token 访问”的问题。


















