Skip to content
Go back

大规模登录系统——从Session到JWT到OAuth2

登录系统设计:从单体 Session 到 OAuth2

一句话结论(30s)

登录系统的演进是”规模问题倒逼认证状态外移”——因为单体 Session 无法水平扩展、Redis 共享 Session 引入单点、JWT 无状态但无法主动失效,最终 OAuth2 把认证委托给第三方,每一级方案都是用”新的代价”换”更高的扩展性”。

核心原理(2min)

底层深入(5-10min)

阶段一:单体 Session

登录 → 服务端创建 Session → SessionID 写入 Cookie
请求 → Cookie 带 SessionID → 服务端查 Session → 返回用户信息

简单,适合单体应用。服务端持有全部状态,可主动销毁任何 Session。

阶段二:Redis 共享 Session

服务无状态化(水平扩展)后,Session 必须共享:

Session → Redis (key: session:{id}, value: {userId, roles, ...})
任意节点查 Redis 获取 Session → 无需粘滞路由

代价:每次请求多一次 Redis 查询。Redis 宕机 → 所有用户被迫重新登录。

思考穿插:为什么”Redis 共享 Session”解决了水平扩展,却又引入了一个新的单点?因为状态从”单个应用内存”集中到了 Redis——服务节点可以随便加了,但 Redis 成了所有节点都依赖的关键路径。这正是系统设计的常态:把瓶颈从一处挪到另一处,而不是消灭瓶颈,代价要心里有数。

阶段三:JWT(无状态)

登录 → 服务端签发 JWT(含 userId + exp + 签名)
请求 → Auth Header: Bearer <jwt> → 服务端验签 → 直接获取 userId

不查库、不查 Redis,每个节点独立验证。 代价:无法主动失效(需维护黑名单)。

思考穿插:为什么 JWT 能做到”无状态”,反而失去了”主动失效”的能力?因为”无状态”意味着服务端不保存任何会话信息——既然没记录”谁在线”,自然就没办法让某个 token 立刻作废。这是”状态外移”的必然代价:状态从服务端挪到客户端,换取水平扩展,却放弃了服务端的控制权。

Refresh Token 机制:Access Token 短有效期(15min),Refresh Token 长有效期(7d)存在 HttpOnly Cookie 中防 XSS。

阶段四:OAuth2 / 第三方登录

用户 → 前端: "用微信登录"
→ 微信授权页 → 用户同意 → 微信服务端返回 code
→ 后端拿 code 向微信交换 access_token
→ access_token 拿用户微信 openid
→ 服务端签发自己的 JWT

标准协议,第三方不拿到用户密码,用户不需要记住新密码。

思考穿插:为什么 OAuth2 能把”认证”这件事委托给第三方,还让第三方拿不到用户密码?因为它的核心是”授权码换 token”的间接流程——用户密码只输给微信,微信只返回一个临时的 code 和 token,业务方拿到的只是”确认过身份”的凭证,而不是密码本身。这层”不碰密码”的设计,是信任与安全的分界点。

小程序登录的特殊性

微信小程序调用 wx.login() 获取临时 code → 后端拿 code 调微信接口换 openid + session_key不需要用户弹出授权框——code 是静默获取的。 要获取用户昵称头像等,需要另外的 <button open-type="getUserInfo"> 用户主动点击。

总结

方案状态位置扩展性可主动失效
Session服务端❌ 单体
Redis SessionRedis
JWT客户端自包含❌(需黑名单)
OAuth2第三方平台第三方控制

章末提问

登录系统设计是经典的系统设计题,对方会顺着”每一级的代价”往下追。以下是三个典型追问:

  1. “单体 Session 为什么不能水平扩展?” 结论先行:「因为 Session 存在单个应用进程的内存里,加机器后请求被路由到别的节点就找不到对应 Session,除非引入粘滞会话或集中存储。」因为 对方在确认你理解了”状态与扩展的矛盾”——状态内嵌在进程里,就是横向扩容的最大障碍,这是后续所有方案演进的起点。

  2. “JWT 无法主动失效,怎么处理’用户退出/改密后旧 token 仍有效’的问题?” 结论先行:「用短有效期 Access Token + 长有效期 Refresh Token,配合服务端黑名单或版本号来标记已作废的 token。」因为 这题在考你能否补上 JWT 的短板——能说出”短过期 + 黑名单”的组合方案,说明你不是只背了 JWT 的优点。

  3. “为什么用 Refresh Token 存在 HttpOnly Cookie 里,而不是 localStorage?” 结论先行:「因为 HttpOnly Cookie 对 JS 不可见,能防 XSS 窃取 token;localStorage 能被脚本直接读取,一旦被注入 XSS 就全泄露。」因为 对方在验证你的安全细节——token 存储位置直接决定它在 XSS 攻击下的暴露面,这个选择背后是明确的攻防考量。


Share this post on:

Previous Post
海量数据处理——从哈希分桶到位图的通用解题框架
Next Post
分布式事务——从2PC到Seata AT的选型全景