上一篇我们讲了 Session + Cookie,那是 Web 登录的开山鼻祖。但当我们把系统拆成微服务之后,Session 的问题就暴露了——Session 存在服务端,服务一多就尴尬了。

一、Session 在微服务下的尴尬

想象一下:用户在订单服务登录了,Session 存在订单服务的内存里。然后他去调商品服务——商品服务说「你是谁?我不认识你。」

怎么办?共享 Session 呗。搞个 Redis 集群,所有服务都去查同一份 Session。

听起来没问题,但服务越来越多,每个服务都要依赖这个 Session 存储,耦合度拉满。Redis 一挂,全体服务瘫痪。

更别说移动端了——App 压根没有浏览器的 Cookie 机制,Session ID 要自己手动传,体验一言难尽。

二、JWT:把身份证揣自己兜里

JWT 的思路完全不同:别存了,把用户信息直接发给客户端,下次请求带回来就行。

就像你去政府办事,Session 是「你先去窗口登记,窗口给你发个号码牌,工作人员拿着号码牌去档案室查你是谁」;JWT 是「你把身份证揣兜里,到了直接亮证,工作人员看一眼就知道你是谁」。

不需要查档案室,信息就在证件上。

三、JWT 长什么样?

JWT 由三段组成,用点号拼接:

eyJhbGci....eyJzdWIi....SflKxwRJ...

对应的是:Header(头部) . Payload(载荷) . Signature(签名)

用比喻来说:

  • Header 是信封,写着「这封信用了什么加密方式」,比如 alg: "HS256"
  • Payload 是信的内容,装着你的身份信息,比如 sub: "user123", role: "admin", exp: 1699000000
  • Signature 是信封上的火漆印章,证明这封信没被篡改过

每一段都是 Base64 编码,不是加密。Payload 里的内容谁都能解码看,所以别往里面塞密码。

四、签名到底防的是什么?

有人可能会问:Payload 又不是加密的,别人改了怎么办?

这就是签名的作用。签名的生成逻辑大致是:

HMAC-SHA256(Header + "." + Payload, 密钥)

服务端收到 JWT 后,用同样的密钥和算法重新算一遍签名。如果和客户端传过来的签名不一致,说明内容被改过了,直接拒绝。

签名不保护隐私,保护的是完整性。

五、双 Token:Access Token + Refresh Token

JWT 有个特点:发出去就收不回来。所以通常不会给一个长有效期,而是搞两个 Token:

  • Access Token:有效期短,比如 15 分钟,用来访问接口
  • Refresh Token:有效期长,比如 7 天,专门用来换新的 Access Token

流程是这样的:

1
2
3
4
5
6
7
登录 → 拿到 Access Token + Refresh Token

请求接口,带上 Access Token

Access Token 过期了?用 Refresh Token 去换一个新的

Refresh Token 也过期了?重新登录吧

请求时长这样:

Authorization: Bearer eyJhbGci...

双 Token 的好处是:Access Token 泄露了,影响范围有限;Refresh Token 只在换 Token 时用,暴露面小。

六、JWT 的阿喀琉斯之踵

JWT 听起来很美好,但它有一个致命缺点:无法主动失效。

Session 的好处是存在服务端,想让用户下线,删掉 Session 就行。JWT 发出去以后,它就脱离你的控制了。

用户改了密码,想让所有设备重新登录?JWT 做不到,除非你等到它过期。Token 还活着,你拿它一点办法都没有。

有人说搞个黑名单,把要失效的 Token 存起来,每次请求都查一下黑名单——那你这不是又回到 Session 的老路了吗?

所以 JWT 和 Session 不是谁替代谁的关系,是不同场景下的不同选择。

七、什么时候用 JWT?

  • 微服务架构,多个服务需要验证身份 → JWT,省去共享 Session 的麻烦
  • 移动端 App → JWT,不依赖 Cookie 机制
  • 需要灵活控制登录状态 → Session,随时能踢人
  • 单体应用,用户量不大 → Session 就够了,别折腾

八、一句话总结

JWT 把身份信息从服务器搬到了客户端,换来了可扩展性,但失去了主动控制权。技术选型永远是权衡,不是银弹。


系列文章