跳至主要內容

十万个why:为什么 JWT 明明主打“无状态、不用查库”,实际业务还是得存个 Redis?

程序员小富大约 4 分钟

大家好,我是小富~

做业务开发有时就是有些矛盾的地方,比如 JWT 都吹它无状态,又不用查数据库性能还好,结果实际开发转头 token 就存 Redis 里了。

JWT(JSON Web Token)自带用户信息和签名,服务端不需要存任何东西,天然支持水平扩展,还能省掉昂贵的数据库查询,这就是无状态架构的优雅之处。

但真实业务中,JWT 的最大优点无状态,反倒成了它最大的缺点!!!

JWT 的设计初衷

JWT 的设计初衷确实非常惊艳,把用户 ID、权限、过期时间全塞进一串字符串里,再用私钥打个签名。

服务器收到后,只要拿着公钥验一下签名对不对,就知道这人是谁。这就像是给用户发了一张通行证,卫兵只需要看证件真伪,不需要打电话回总部确认。

听起来都挺好,但这带来了一个致命的权力真空,服务端失去了对令牌的主动控制权

为什么无状态在业务里不行?

在真实的业务逻辑里,我们对鉴权的需求不仅仅是验真,更核心的需求是管控

就比如以下三个场景,是纯 JWT 根本做不到的

主动注销与踢人下线

在 Session 模式下,我们删掉 Redis 里的 Session 就行了。

但在纯 JWT 模式下,令牌发出去就像泼出去的水。如果用户点退出登录,或者管理员想强行踢掉一个违规用户,只要令牌还没到过期时间,用户依然能拿着它调用你的接口。

作为系统的主人,是一点办法都没有啊。

密码修改

用户发现账号被盗,紧急修改了密码。这时如果你没在后台记录令牌状态,黑客手里那个还没过期的 JWT 依然是有效的。这种逻辑上的不一致性在金融或核心业务里是 P0 级的安全事故。

续期问题

为了安全 JWT 过期时间通常设得很短,比如 30 分钟,但又总不能让用户每半小时重新登录一次。

为了实现这种无感续期,必须引入 Refresh Token,一旦涉及到 Refresh Token 的状态维护,发现绕了一大圈,最后还是得找个地方把它存起来。

Redis 到底在存什么?

完全无状态是行不通的,这时候 Redis 成了必然的选择,目前主流做法主要分两个段位:

黑名单机制

平时不去查 Redis,只有当用户退出或改密码时,把该 Token 的标识塞进 Redis 设为黑名单。校验时先看签名再去 Redis 瞅一眼。

代价依然要查一次库,JWT 的无状态优势弱了一半。

白名单与状态管理

这也就是大家最常用的 JWT 只是个载体,Redis 里存着对应的状态。

这本质上是把 JWT 当成了 Session ID 在用。

既然都用 Redis 了,为什么还要用 JWT?

看到这,大家就迷惑了:既然最后都要查 Redis,那我直接用传统的 Session 方案不是更香吗?为什么要给 Token 加上那么长的 Header 和 Payload,浪费带宽?

我觉得 JWT 即便引入了 Redis,依然有三个 Session 无法比的优势:

  1. 数据的内聚性:JWT 里带了用户 ID、头像、昵称等基础信息。这让网关在做限流、灰度路由时,不需要去查缓存,就能直接拿到用户信息进行决策。这种预读取的能力,对微服务集群的性能优化极其关键。

  2. 跨服务的信任链 :几十个微服务之间跳转,我们只需要传递这串 Token。只要大家都信任发证的那个 Key,就不需要每个服务都去连一次 Redis 验真。只需要在网关层查一下 Redis 黑名单,剩下的服务节点只验签名,效率性能都很高。

  3. 多端灵活度:对于 App、小程序、Web 等多端混杂的场景,JWT 这种放在 Authorization Header 里的方式,比依赖 Cookie 的 Session 要灵活得多。

说在最后

Redis 不是 JWT 的补丁,应该是算是 JWT 的控制器。

架构设计从来不是为了追求完美,是为了解决具体的业务问题。

上次编辑于: