一文彻底搞懂 JSON Web Token (JWT) 的认证原理

在前后端分离架构和微服务大行其道的今天,如果你还在所有的项目中死抱传统的 Session/Cookie 方案不放,那你可能已经落后于时代了。今天,我们要聊的主角就是:JSON Web Token (通常简称为 JWT)

但千万不要误会我的意思:Session 并没有死。JWT 有它最闪光的舞台,也有它被滥用导致的灾难。如果你去过面试,考官一定会问:“JWT 是什么?它和 Session 的区别是什么?” 读完这篇文章,保证你不仅能对答如流,还能在实战中少走弯路。

1. 告别有状态:Session 的痛点

在传统的 Web 世界里,认证是有状态(Stateful)的。你登录了一个网站:

  1. 服务器验证你的账号密码。
  2. 服务器在它那边的内存或者 Redis 里划出一块小空间,记下“用户 A 已经登录”,并生成一个对应的 Session ID。
  3. 服务器把这个 Session ID 塞进响应的 Cookie 里发给浏览器。
  4. 浏览器以后每次发请求都会带上这个 Cookie,服务器收到后再去内存翻本子查:“哦,这个 ID 是用户 A”。

在单体应用时代,这很完美。但当你的业务变大,你的后端从一台机器扩展到了十台、几十台集群,并且有了手机 App 客户端甚至第三方平台。问题来了:跨服务器不仅需要复杂的 Session 同步逻辑,有些非浏览器客户端连处理 Cookie 都嫌麻烦。

2. 拥抱无状态:JWT 提供的一把自解释钥匙

既然服务器不想记账了,我们能不能让客户端自己拿着账本,每次来的时候自己把底细交代清楚?这就引出了无状态(Stateless)认证,即 JWT。

登录成功后,服务器会把用户的身份信息(比如 User ID = 123,角色 = Admin)打个包,盖上一个别人无法伪造的公章,发给客户端。客户端把这串数据存起来(比如存在 LocalStorage ),以后每次请求都在 HTTP Header 里带上它。服务器收到了以后,不用查任何数据库,只要看这上面的公章是真的,直接信任并提取里面的信息。

3. JWT 的内部解剖:它到底是由什么构成的?

你在浏览器的请求头中看到的 JWT 往往是这样一长串字符(随便在某个系统抓个包):

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.
TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

仔细看,它由三个部分组成,由两个点 . 分隔。分别是 Header(头部)Payload(负载)Signature(签名)

第一部分:Header (头部)

通常是一个 JSON 对象,告诉你这是个 JWT,并且签名用的是什么算法(比如 HMAC SHA256 或者 RSA)。将这个 JSON 进行 Base64Url 编码后,就构成了第一段。

{
  "alg": "HS256",
  "type": "JWT"
}

第二部分:Payload (负载)

这里装着核心数据,在规范中被称为“声明(Claims)”。主要分为标准声明(如发证人 iss、过期时间 exp)和自定义声明(比如你放进去的用户 idemail)。同样,这也只是一串经过 Base64Url 编码的 JSON。你可以在我们的 JWT Decoder 工具 中随时验证和解码任何一段合法的 JWT Payload。

安全警告: Payload 只是 Base64 编码,并不是加密!这也就意味着任何人只要拿到了你的 Token,都能像明书一样读到里面的内容。因此,绝对不要把密码、银行卡号等敏感隐私信息放进 Payload 里。

第三部分:Signature (签名)

既然前两部分都是明文,如何防止黑客篡改内容(比如他把 Payload 里的 "admin": false 改成了 true)呢?这就是最后一部分的作用。

服务器在签发时,会将前两部分通过指定的散列算法,加上一把只有服务器自己知道的神秘私钥 (Secret),计算出一个哈希串。如果黑客篡改了 Payload,但他不知道这把私钥,他就无法重新计算出正确的签名。服务器在收到 Token 重新进行同样的哈希计算比对时,就会发现对不上,直接把它扔进垃圾桶。

4. 使用 JWT 的优缺点与避坑指南

好处:

  • 横向扩展容易:完全不需要维护 Session 状态,跨集群无需做任何黏性会话或者 Redis 集中管理的复杂工作。
  • 防 CSRF:如果不在 Cookie 中存放,并且采用 Authorization 头部的 Bearer 方案传输,就自然免疫了跨站请求伪造攻击。
  • 性能极高:服务器减少了一次 I/O 去缓存或者数据库查询会话。

致命弱点:

  • Token 无法被主动废弃(除非结合黑名单):因为状态都在客户端。一旦颁发了一个有效时间为 1 个月的 Token,如果用户在这期间改密码、或者离职了,这个旧的 Token 在过期前理论上都是有效的。要解决这个问题,就不得不引入 Redis 做黑名单校验——但这又彻底违背了 JWT “无状态” 的初衷!
  • Token 过大:相比只有 32 位的随机 Session ID,随便一个 JWT 携带一些信息后可能长达四五百字节。每一次 API 请求都携带这个大头绪会造成一定的带宽浪费。

总结

工具并没有好坏,只有是否适合场景。如果你只是开发一个纯正的前后端分离的中小型业务系统,或者是微服务之间的内部调用凭证校验,JWT 绝对是开发利器。但是如果你做的是对账户实时状态要求极高的金融或高安全性质系统,老老实实回到 Stateful Session 或者是 Redis + Token 的架构上,也许才是正道。

想要亲自解析一个 JWT 验证上面的知识吗?试试我们本站的 在线 JWT 解析器