新手也能看懂的 JWT 讲解:我踩完坑才搞明白

3 阅读5分钟

先说个事:HTTP 是无状态的。

翻译成人话:你登录成功了,但服务器转头就把你忘了。你下次再来,它又问:“你谁啊?”

传统做法是 Session:服务器拿个小本本记着你,给你一个 sessionId,你每次来报这个号。但服务器一多,小本本就得复印好几份,还得用 Redis 这种公共小本本同步,麻烦。

JWT 的思路很野:不记了。我把你的信息写在一张通行证上,签个名,你自己拿着。下次你来,我验一下签名就行,不用查本子。

这就是它最大的好处:正常情况下,服务器不用存 token,天生适合分布式。

如果你只记一句话:

JWT 是一张签了名的电子通行证。内容透明,但伪造不了。

读完这篇,你能搞懂三件事:

  1. JWT 到底是什么?
  2. 它为什么适合分布式?
  3. 新手最容易踩哪 3 个坑?

JWT 长啥样?

就一串字符,两个点分成三段:

xxxxx.yyyyy.zzzzz

这三段分别是:

Header:说明用什么算法签名 Payload:存你的信息,比如用户 ID、过期时间 Signature:用密钥对前两段签名,防篡改

重点来了:Payload 只是 Base64 这类编码,不是加密。

你可以把 Base64 理解成“把文字换成另一种写法”,它可逆,谁都能还原。不是加密,挡不住人看。

所以——千万别往 Payload 里塞密码、手机号、身份证。这玩意谁都能看。

签名的作用是:内容公开,但改不了。你改了 userId,签名就对不上,服务器直接拒绝。

一句话:

Header 说算法,Payload 放信息,Signature 防篡改。

Session 和 JWT 到底差在哪?

状态存哪: Session 存在服务器。 JWT 存在客户端。

服务器要不要记: Session 要记。 JWT 正常不用记。

适合场景: Session 适合需要强制下线、集中管理登录状态的场景。 JWT 适合分布式、无状态、前后端分离、多服务器、不想让服务器存登录状态的场景。

最大缺点: Session 的缺点是服务器有状态,扩展麻烦。 JWT 的缺点是不能天然强制下线,Payload 可读。

所以不是谁一定更好,而是场景不同。

在我项目里怎么跑的?

拿我之前做的一个前后端分离小项目“校缘智链”的登录流程举例:

  1. 用户提交用户名密码
  2. 后端验证通过,生成 JWT 返回前端
  3. 前端存起来,我当时用的 localStorage
  4. 之后每次请求,请求头带上:

httpAuthorization: Bearer

  1. 后端验签名 + 查过期时间,通过就放行

后端不存这个 token,验签名就知道:“这确实是我签发的,没被改过。”

记住这句:

JWT 是无状态的,后端不认识你,只认识 token。

我踩过的三个坑

坑 1:密钥太短,启动就报错

我随手写了个短密钥,启动直接报 WeakKeyException,提示密钥只有 176 位,不够 256 位。

HS256 算法要求密钥至少 256 位。 如果你直接把普通字符串当密钥,1 个 ASCII 字符大约等于 1 字节,也就是 8 位,所以至少需要 32 个字符。

我后来换成随机生成的 44 字符 Base64 密钥,解码后正好 32 字节,才正常。

新手别纠结“位”和“字符”的换算,记住结论就行:

JWT 密钥别抠门,至少 32 个字符起步,最好用密码生成器生成随机串,而且别写死在代码里,放环境变量。

坑 2:前端忘带 token,一直提示“未登录”

我明明登录了,发布商品却返回“未登录”。

排查半天,发现前端 fetch 请求头里根本没加 Authorization。加上这行就好了: 'Authorization': 'Bearer ' + localStorage.getItem('token')

原因很简单:

JWT 是无状态的,前端每次请求都得自己带着 token。后端不认识你,只认识 token。

坑 3:OPTIONS 请求被拦截,跨域直接挂了

前后端分离,跨域请求时,浏览器会先发一个 OPTIONS 预检请求。

你可以把 OPTIONS 理解成“侦察兵”:它先跑去问服务器:“我能发这个 POST 请求吗?”

这个侦察兵通常不带 token。 我的拦截器一视同仁,所有请求都查 token。结果 OPTIONS 被拦下返回 401,真正的 POST 请求根本发不出去。

解决:在拦截器里放行 OPTIONS。

再配上 CORS 配置,搞定。

这个坑含金量很高,面试被问到跨域可以直接讲。

面试常问的两个问题

  1. JWT 和 Session 有什么区别?

Session 服务器存状态,JWT 客户端存状态。 JWT 适合分布式、无状态场景;Session 适合需要强制下线的场景。

  1. JWT 能强制下线吗?

不能,这是它最大的缺点。

Token 签发后,过期前一直有效。想强制失效,得在服务端维护黑名单——但这就又回到“服务器存东西”了,违背 JWT 初衷。

所以一般配合短过期时间 + refresh token 使用。

新手最容易误解的四件事

  1. 以为 JWT 是加密的 不是,只是签名。Payload 谁都能看,别放敏感信息。
  2. 密钥写死提交到 Git 密钥泄露 = 别人能伪造任意 token。必须放环境变量。
  3. 不设过期时间 exp 字段必须加,不然 token 永久有效。
  4. 存 localStorage 有 XSS 风险。更安全的是 httpOnly cookie,但知道有风险、知道怎么选就行。

总结一句话

JWT 就是一张签了名的电子通行证,服务器不存底,靠签名防伪造。Payload 公开可读,别放敏感信息。最大缺点是没法天然强制下线。

最后,给你一个行动清单

现在打开你的项目,检查 4 件事:

  1. 密钥是不是至少 32 个字符?
  2. 密钥有没有放环境变量,而不是写死在代码里?
  3. 前端请求有没有带 Authorization: Bearer ?
  4. 拦截器有没有放行 OPTIONS?

3 道自测题

  1. JWT 能放密码吗?
  2. JWT 能强制下线吗?
  3. 前端每次请求要带什么请求头?(划慢点,后面就是答案)

答案:

  1. 不能,Payload 只是编码,谁都能看。
  2. 不能天然强制下线,除非做黑名单或短过期 + refresh token。
  3. Authorization: Bearer 。