jwt单点登录通过设备指纹+redis实现踢旧会话:用ip、ua、设备类型生成device_id存自定义claim,redis以user:uid:device:did存jti,登录时删旧token,校验时查jti有效性。

JWT Token 中如何携带设备标识并校验唯一性
单点登录限制的本质不是“禁止重复登录”,而是“踢掉旧会话”。Gin 本身不处理会话生命周期,得靠 JWT 载荷(claims)和后端存储协同实现。关键在于:每次登录必须生成带设备指纹的 Token,并在签发时记录该设备 ID + 用户 ID 的映射关系。
设备标识不能只依赖前端传来的 User-Agent(易伪造),建议组合以下字段生成哈希:ip + user_agent + device_type(如 web / ios / android)。服务端用 sha256.Sum256 计算固定长度指纹,存入 Redis 的键如 user:123:device:abc123,值为当前 Token 的 jti(JWT 唯一 ID)。
常见错误是把设备 ID 放进 sub 或 iss 字段——这些字段语义固定,应严格按 RFC 7519 使用。正确做法是扩展自定义 claim,例如:device_id、login_at(时间戳)、exp(配合 Redis 过期时间对齐)。
Gin 中间件如何拦截并踢出旧设备 Token
用户再次登录时,先根据 user_id 查询 Redis 中所有已存的设备记录,逐个调用 redis.Del() 删除旧 Token 对应的 key(如 token:xxx),再把新 Token 写入。注意:必须在签发新 Token 前完成清理,否则会出现窗口期冲突。
中间件验证 Token 时,除了标准 jwt.Parse 和签名校验,还需额外查 Redis 确认该 jti 是否仍有效。若返回 nil(Key 不存在),说明已被踢出,直接返回 401 Unauthorized 并附带错误信息 "token revoked"。
要点:
- Redis key 设计要支持快速扫描:用
user:{uid}:devices存设备 ID 列表,每个设备 ID 对应一个token:{jti}的 string 类型 key - 不要用
GETALL拉全量数据再遍历,应直接DEL token:{old_jti} - Redis 过期时间必须略长于 JWT 的
exp(比如 +30s),防止因时间差导致误判
前端登出时如何确保服务端同步失效 Token
前端调用登出接口,本质是让服务端把当前 Token 加入黑名单(或删除对应设备记录)。但 JWT 本身无状态,无法“主动作废”,只能靠外部存储拦截。
推荐做法:登出请求携带 Authorization 头中的 Token,后端解析出 jti 和 device_id,执行 redis.Del("token:" + jti) 和 redis.LRem("user:{uid}:devices", 0, device_id)。如果用的是黑名单模式,就写入 blacklist:{jti} 并设 TTL 等于剩余过期时间(需从 exp 减去当前时间计算)。
容易踩的坑:
- 前端清空 localStorage 后没调登出接口 → 服务端 Token 仍有效 → 其他设备登录时不会踢它
- 登出接口未校验用户身份,仅凭 Token 就删 —— 必须验证
sub和当前请求用户一致 - 没做幂等处理,重复登出导致 Redis 报错(
DEL本身幂等,但LRem需判断返回值是否 -1)
多终端共存但限制同设备多登录的边界逻辑
“单点登录”常被误解为“一人只能一个会话”,实际业务往往需要:同一用户可在 iPhone、iPad、Web 同时在线,但不允许两个 Chrome 标签页都保持登录。这就要求设备类型粒度可控。
解决方案是在生成 device_id 时加入可配置的粒度开关:
- 若需“每浏览器实例独立”,则加入
localStorage生成的随机 salt(首次访问写入,后续复用) - 若需“每设备独立”,则去掉 IP,只用
user_agent+device_type做哈希 - Gin 解析时通过 query 参数或 header(如
X-Device-Mode: instance)动态决定策略
这个逻辑必须前后端约定清楚,否则前端传错 device_type(比如把安卓 WebView 识别成 mobile)会导致误踢。最稳妥的方式是服务端根据 User-Agent 自动归类,不依赖前端上报。
真正麻烦的是 Token 续期场景:refresh token 机制下,新 Access Token 的 device_id 必须和原 Token 一致,否则会被当成新设备踢掉自己。续期接口务必透传原始设备指纹,而不是重新生成。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











