jwt中间件必须校验redis会话存在性,否则解析成功不等于登录合法;应使用扁平化key如session:123:abc123存完整token+设备信息+时间戳,禁用hset哈希结构,退出时需双向清理前端token与redis会话。

中间件里只校验JWT签名和过期,不查Redis会话就等于没限制
JWT本身无状态,jwt.ParseToken 解析成功 ≠ 当前登录合法。常见错误是:解析完直接 next(c),完全没查这个 token 是否已被新登录覆盖。结果就是“踢人”逻辑形同虚设,用户在手机、电脑同时在线毫无感知。
必须在中间件中补上 Redis 会话存在性校验——即用请求头里的 token 去 Redis 查它是否仍属于该用户的当前有效会话。
- 登录成功后,把
session:<userid>:<sessionid></sessionid></userid>存为 string 类型(不是 hash),value 是完整 token 字符串 + 设备信息 + 登录时间戳 - 中间件里先解析出
userID和token,再拼出 key:session:123:abc123,执行GET操作 - 如果返回空或值不匹配,立即返回
401 Unauthorized,并提示“账号已在其他设备登录”
为什么不能用 HSET user:123 sessions "abc123" "" 存多设备?
用哈希结构存会话看似自然,但实际踩坑严重:HGETALL 性能随字段数线性下降;DEL 整个 hash 无法原子性清理旧会话;更关键的是——你没法用单条命令“根据 token 反查 userID”,而鉴权时你只有 token,没有 sessionID。
推荐结构是扁平化 key:session:<userid>:<sessionid></sessionid></userid>,配合在 JWT payload 中嵌入 session_id 字段。这样每次请求都能用 userID + session_id 精准定位唯一会话记录。
Echo框架 5.1.0 版本源码包下载,适合关注 RealIP 行为变化、StartConfig.Listener、NewDefaultFS 和观测性中间件入口的开发团队。
- 避免用
HSET user:123 sessions abc123 ""—— 删除旧会话需先HKEYS再遍历HDEL,非原子且慢 - 不要把 token 存进 hash 的 value,而应存完整 token 字符串,用于后续严格比对
- 登录时先
DEL user:123:sessions清旧,再写新 key,确保最终一致性
生产环境多实例部署,sync.Map 限流中间件会彻底失效
如果你在中间件里用 sync.Map 做 IP 或用户级限流(比如 rate.NewLimiter),那在多台 Echo 实例下,每个进程维护独立桶,同一 IP 的请求打到不同机器就绕过限制了。
同一账号多地登录的互斥控制也一样:必须把“当前有效会话”状态提到共享层,Redis 是唯一靠谱选择。别幻想靠内存或本地文件同步。
- Redis 连接池要配置合理,超时建议 ≤ 50ms,否则中间件自身成瓶颈
- 用 Lua 脚本封装“读 token → 比对 → 返回结果”逻辑,避免网络往返和竞态
- key 命名统一加业务前缀,如
auth:session:123:abc123,方便后期扫描清理
退出登录时只删前端 token,Redis 里 session 还挂着就是重大漏洞
用户点“退出”,前端清了 localStorage 或删了 cookie,但服务端 Redis 里 session:123:abc123 依然存在。下次攻击者拿着旧 token 重放,照样能通过鉴权。
退出逻辑必须双向清理:前端清除凭证 + 后端 DEL 对应 session key。而且这个 DEL 必须带 userID,不能只依赖前端传来的 token(可能被篡改)。
- 退出接口收到请求后,先解析 JWT 得到
userID和session_id,再拼 key 执行DEL - 别信前端传来的任意 token 字符串做 key 查询,必须从已签名 payload 中安全提取字段
- 若支持“踢他人下线”,则需根据
userID扫描所有session:123:*key 并批量删除










