先说个事:HTTP 是无状态的。
翻译成人话:你登录成功了,但服务器转头就把你忘了。你下次再来,它又问:“你谁啊?”
传统做法是 Session:服务器拿个小本本记着你,给你一个 sessionId,你每次来报这个号。但服务器一多,小本本就得复印好几份,还得用 Redis 这种公共小本本同步,麻烦。
JWT 的思路很野:不记了。我把你的信息写在一张通行证上,签个名,你自己拿着。下次你来,我验一下签名就行,不用查本子。
这就是它最大的好处:正常情况下,服务器不用存 token,天生适合分布式。
如果你只记一句话:
JWT 是一张签了名的电子通行证。内容透明,但伪造不了。
读完这篇,你能搞懂三件事:
- JWT 到底是什么?
- 它为什么适合分布式?
- 新手最容易踩哪 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 可读。
所以不是谁一定更好,而是场景不同。
在我项目里怎么跑的?
拿我之前做的一个前后端分离小项目“校缘智链”的登录流程举例:
- 用户提交用户名密码
- 后端验证通过,生成 JWT 返回前端
- 前端存起来,我当时用的 localStorage
- 之后每次请求,请求头带上:
httpAuthorization: Bearer
- 后端验签名 + 查过期时间,通过就放行
后端不存这个 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 配置,搞定。
这个坑含金量很高,面试被问到跨域可以直接讲。
面试常问的两个问题
- JWT 和 Session 有什么区别?
Session 服务器存状态,JWT 客户端存状态。 JWT 适合分布式、无状态场景;Session 适合需要强制下线的场景。
- JWT 能强制下线吗?
不能,这是它最大的缺点。
Token 签发后,过期前一直有效。想强制失效,得在服务端维护黑名单——但这就又回到“服务器存东西”了,违背 JWT 初衷。
所以一般配合短过期时间 + refresh token 使用。
新手最容易误解的四件事
- 以为 JWT 是加密的 不是,只是签名。Payload 谁都能看,别放敏感信息。
- 密钥写死提交到 Git 密钥泄露 = 别人能伪造任意 token。必须放环境变量。
- 不设过期时间 exp 字段必须加,不然 token 永久有效。
- 存 localStorage 有 XSS 风险。更安全的是 httpOnly cookie,但知道有风险、知道怎么选就行。
总结一句话
JWT 就是一张签了名的电子通行证,服务器不存底,靠签名防伪造。Payload 公开可读,别放敏感信息。最大缺点是没法天然强制下线。
最后,给你一个行动清单
现在打开你的项目,检查 4 件事:
- 密钥是不是至少 32 个字符?
- 密钥有没有放环境变量,而不是写死在代码里?
- 前端请求有没有带 Authorization: Bearer ?
- 拦截器有没有放行 OPTIONS?
3 道自测题
- JWT 能放密码吗?
- JWT 能强制下线吗?
- 前端每次请求要带什么请求头?(划慢点,后面就是答案)
答案:
- 不能,Payload 只是编码,谁都能看。
- 不能天然强制下线,除非做黑名单或短过期 + refresh token。
- Authorization: Bearer 。