redis 必须作为状态中心实现真正单点登录,jwt仅作无状态凭证;需用自定义前缀和db的redis store隔离会话,登录时扫描并删除用户旧session key,token应通过authorization header传递而非cookie。

Redis 必须作为状态中心,不能只用 JWT 签发
纯 jwt-go 签发的 token 无法实现真正的单点登录——用户在设备 A 登出后,设备 B 的 token 仍能通过 jwt.Parse 校验,直到 exp 到期。这不是 SSO,是“单点登出失效”。
真正起作用的是 Redis:它存储活跃会话、支持 token 吊销、同步多实例状态、支撑 use-multipoint: true 配置生效。
所以第一步不是写 jwt.Sign,而是确认 Redis 连接已就绪、DB 可写、key 命名空间不冲突。
gin-contrib/sessions/redis 存储需自定义前缀与 DB 选择
gin-contrib/sessions/redis 默认把所有 session 存进 db 0 且无前缀,多个服务共用 Redis 时极易 key 冲突。
必须手动封装 NewStoreWithDBPrefix,从配置读取 db 和 prefix:
– 示例中配置 db: 3、prefix: "demo",实际生成的 session key 就是 demo:session:xxx
– 不要用默认 redis.NewStore,否则 Store.Get(c, "session") 读到的可能是其他服务的 session
– 多项目共用 Redis 时,不同 prefix 是隔离前提,否则 SSO 会跨业务串号
登录成功后必须清除用户旧会话,才能保证单点性
SSO 的核心逻辑不在签发新 token,而在“踢掉旧登录”:
– 用户本次登录前,先用 redis.Keys("demo:session:*user_id_123*") 扫描该用户所有活跃会话 key
– 对每个匹配 key 执行 redis.Del,强制使旧 session 失效
– 再调用 session.Set("user_id", 123) + session.Save() 写入新会话
– 若跳过这步,同一用户多端登录后 logout 某一端,其余端仍保持登录,违反 SSO 定义
– 注意:扫描 key 有性能开销,生产环境建议改用 HSCAN 或按 user_id 建 hash 结构索引
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
token 应该放在 Authorization Header,别塞 Cookie
服务端签发的 JWT,强烈建议通过 Authorization: Bearer xxx 传递,而不是 http.SetCookie:
– Cookie 方式在跨域场景下受 SameSite 限制,前端 fetch 时需显式配 credentials: 'include',且 Nginx 反向代理容易静默丢 query 或 cookie
– Gin 默认不设 SameSite=Lax,Cookie 易被 CSRF 利用;而 Header 由前端主动携带,可控性更强
– 如果必须用 Cookie,请确保 http.SameSiteStrictMode + Secure: true + HttpOnly: false(因 JS 需读取 token 刷新)
– 最关键一点:OIDC 场景下,id_token 本就是设计为 Header 传的,和 SSO 会话 token 分离更清晰
真正难的不是写几行 redis.Set,而是厘清“谁负责吊销”“谁负责扫描”“谁负责跨实例同步”。JWT 解析快,但状态管理慢;Redis 速度快,但 key 设计错一步,整个 SSO 就变成多点登录。










