结论:Echo中双Token认证的核心是/auth/refresh接口必须原子性完成校验、作废、签发三步;漏掉任一环即导致安全漏洞。

直接说结论:在 Echo 框架中实现双 Token(access_token + refresh_token)认证,核心不是“怎么发两个 token”,而是 /auth/refresh 接口能否原子性完成校验、作废、签发三件事——漏掉任意一环,就等于把钥匙交给攻击者还忘了锁门。
为什么 Echo 的中间件链容易导致 refresh_token 校验失效
Echo 默认的中间件执行顺序是线性的,但 /auth/refresh 必须在单事务内完成“查库→比对指纹→检查用户状态→删旧 token→写新 token”。如果把校验逻辑拆到多个中间件(比如一个做 JWT 解析,一个查数据库,一个做设备比对),并发刷新时极易出现竞态:两个请求同时读到同一个未作废的 refresh_token,都成功换出新 access_token,旧 token 却仍有效。
实操建议:
- 把整个刷新逻辑收进一个 Handler 函数,用
db.BeginTx()包裹全部数据库操作,失败则Rollback - 避免在中间件里解析
refresh_token后只存入c.Set()——中间件间传递明文或结构体可能被意外复用 - 不要依赖
c.Get("user_id")做后续校验,必须重新从数据库查出绑定的fingerprint和expires_at
refresh_token 存 Redis 还是 PostgreSQL?关键看作废粒度
如果你的系统需要支持“单设备登出”或“所有设备强制下线”,必须用关系型数据库(如 PostgreSQL)存 refresh_token 记录;若只要“过期即失效”,Redis 更快但无法精准 revoke。
常见错误现象:用 SET auth:rt:{user_id} {token_hash} EX 2592000 存 Redis,结果用户点“退出登录”时只能删 key,无法区分是当前设备还是全部设备。
Token 消耗追踪工具。当用户需要监控 API token 使用情况、生成每日/每周消耗报告、分析使用趋势时使用此 Skill。功能包括:自动记录每次对话的 token 消耗、生成数据表格报告、计算使用趋势和预测、支持定时推送日报。
实操建议:
- 用 PostgreSQL 表
user_refresh_tokens,字段至少含:id、user_id、token_hash(sha256(refresh_token))、fingerprint(sha256(ip + user_agent))、revoked(bool)、expires_at - 刷新时先
SELECT ... FOR UPDATE锁住对应记录,再判断revoked = false AND expires_at > NOW() - 作废旧 token 不是
DELETE,而是UPDATE ... SET revoked = true,方便审计和重放防护
access_token 过期后前端怎么无感刷新?别信“401 自动重试”
前端监听 401 后直接拿本地存的 refresh_token 请求新 access_token,看似无感,实则埋雷:多个并行请求同时收到 401,会触发多次刷新,导致部分请求带着已作废的旧 access_token 重发,被拒绝后用户看到空白页或弹窗。
实操建议:
- 前端维护一个刷新锁:
let refreshing = false; let pendingRequests = [],首个 401 触发刷新,其余请求进队列,等新 token 返回后统一重放 -
refresh_token必须存在HttpOnlyCookie 中(SameSite=Strict+Secure),不能放localStorage——XSS 可直接盗取 - 后端返回新
access_token时,必须同时返回新的refresh_token(单次有效),前端立即更新 Cookie 值,否则下次刷新仍用旧值
最易被忽略的点:refresh_token 的 fingerprint 必须包含可变因子。只存 IP 或只存 User-Agent 都不行——NAT 网关后所有用户 IP 相同,浏览器自动更新 UA 会导致合法刷新失败。推荐拼接 ip + substring(user_agent, 0, 32) + device_id_header 再哈希,且每次刷新都重新计算并更新数据库字段。

















