JWT 是什么?三段式结构、签名原理与 6 个安全坑一次讲透

一、为什么会有 JWT?

传统 Session 认证流程:登录成功 → 服务端在内存存一份 Session → 给浏览器一个 session_id → 后续请求靠 session_id 查身份。单体应用没问题,但一旦拆成微服务或做水平扩容,Session 在服务 A 存了、路由到服务 B 就读不到;要么引入 Redis 共享存储,要么换方案。

JWT(JSON Web Token,RFC 7519)就是为此生的:它把身份信息直接编码进令牌本身,服务端不存任何会话状态------拿到令牌、验签通过、读 claims,三步完事,天然适合分布式和跨域。

二、JWT 长什么样?三段式结构

一个 JWT 就是由点(.)分隔的三段字符串:

css 复制代码
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjMiLCJuYW1lIjoiQWxpY2UifQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
│                Header                 │                Payload                 │          Signature
部分 作用 编码方式
Header 声明令牌类型和签名算法 Base64URL
Payload 存放声明(claims),即要传递的数据 Base64URL
Signature 防篡改的签名 密钥 + 算法算出

第一段 Header(解码后):

json 复制代码
{ "alg": "HS256", "typ": "JWT" }
  • alg:签名算法(HS256 / RS256 / ES256 等)
  • typ:令牌类型,固定 JWT

第二段 Payload(解码后):

json 复制代码
{ "sub": "123", "name": "Alice", "iat": 1737529242, "exp": 1737532842 }

常用标准声明:

字段 含义
iss 签发者
sub 主题,通常是用户 ID
aud 接收方(受众)
exp 过期时间(Unix 时间戳)
iat 签发时间
nbf 生效时间(之前无效)
jti 唯一 ID,用于防重放

三、最关键的一点:JWT 是"签名"不是"加密"

这是 90% 初学者的误区,也是最大的安全隐患:

Header 和 Payload 只是 Base64URL 编码,任何人拿到令牌都能直接解码读取,它并没有被加密。

只有 Signature 是"签名"------用来证明令牌没被篡改 、且确实由你信任的签发方发出。所以:

  • 解码 ≠ 验证:解码只是把 Base64 还原成 JSON,任何人都能做;验证才需要密钥重新算签名比对;
  • 千万不要在 Payload 里放密码、手机号、身份证号等敏感信息------否则等于明文公开。

如果确实需要内容保密,那得上 JWE(JSON Web Encryption),标准 JWT(JWS)默认不加密。

四、签名是怎么算出来的?

以最常用的 HS256 为例:

scss 复制代码
Signature = HMAC-SHA256( base64UrlEncode(Header) + "." + base64UrlEncode(Payload), secret )

服务端用密钥算一遍,把三段拼起来发给客户端。客户端下次带着令牌来,服务端用同一个密钥、同样的方式重新算一遍签名,和令牌里的签名比对------一致说明没被改过。

黑客想伪造?把 role: "user" 改成 role: "admin" 很容易,但他没有密钥,算不出匹配的签名,服务端验签立刻失败、直接拒绝。这就是 JWT "无法伪造" 的原理。

五、HS256 / RS256 / ES256 怎么选?

算法 类型 密钥 适用场景
HS256 对称(HMAC-SHA256) 单一共享密钥 单体应用、同一服务签发并验证
RS256 非对称(RSA) 私钥签名、公钥验证 微服务、第三方验证(公钥可公开)
ES256 非对称(ECDSA) 私钥签名、公钥验证 高安全、密钥更短、性能更好

简单判断:只有你一家签发并验证 → HS256;多个服务或对外验证、不想泄露密钥 → RS256 / ES256(用 JWKS 端点分发公钥即可)。

六、典型工作流

c 复制代码
用户登录 → 服务端校验账号密码 → 签发 JWT → 前端存起来
前端请求 → Authorization: Bearer <token> → 服务端验签 + 查 exp → 通过则处理业务

AccessToken 一般设 15--60 分钟过期,配一个可撤销的 Refresh Token 做长会话------这是主流做法。

七、6 个真实安全坑(重点)

坑 1:把 JWT 当加密用,敏感信息进 Payload 后果:密码 / PII 直接暴露。Payload 只放非敏感声明(user_id、role),敏感数据服务端现查。

坑 2:只解码不验签 后果:信任了被篡改的数据。任何请求都必须先验签,解码只是为了看内容。

坑 3:alg: none 与密钥混淆攻击 后果:攻击者把 Header 的 alg 改成 none 删掉签名,或把 RS256 的公钥当 HS256 的密钥来重签,伪造管理员令牌。防御:服务端固定算法白名单,绝不听令牌自己声明的算法。

坑 4:HS256 弱密钥 后果:密钥是 secret123456 这类弱值,被 jwtcrack 爆破后任意伪造。防御:HS256 密钥至少 256 位(32 字节) 且随机生成,硬编码进源码更危险。

坑 5:不校验 exp 后果:部分库解码默认不查过期,令牌永久有效。防御:每次验证显式校验 exp(以及 iss、aud)。

坑 6:localStorage 存 Token + 无法主动失效 后果:localStorage 可被 XSS 脚本读取;JWT 无状态,签发后过期前无法"踢人"。防御:AccessToken 优先放 HttpOnly Cookie (防 XSS,配 CSRF 防护);需要即时撤销时用 JWT + Redis 黑名单 / 版本号 混合方案。

八、动手:在线 JWT 解码 / 验证器

贴一个 Token 就能看 Header、Payload、过期时间,还能用密钥验证签名是否有效------纯前端实现,令牌不出浏览器

👉 在线 JWT 解码器0x0bee.com/zh-CN/tools...

  • 解码 :把 JWT 拆成 Header / Payload / Signature 三段,JSON 化展示,一眼看清 exp 是否过期、sub 是谁;
  • 验证(可选):填入密钥与算法,工具用同一算法重算签名,告诉你"签名有效 / 无效";
  • 编码:配套还可在同一工具里构造 Token,调试后端签名逻辑很方便;
  • 安全:计算过程全在浏览器本地完成,不把你的令牌发到任何服务器。

九、常见问题 FAQ

Q1:JWT 是加密的吗?可以直接放敏感数据吗? A:默认(JWS)是签名不是加密,Payload 任何人可解码。不要放密码等敏感信息;要保密请用 JWE。

Q2:解码和验证有什么区别? A:解码是 Base64 还原,不需要密钥;验证要用密钥重算签名并比对,才是可信的。永远先验签再信内容。

Q3:JWT 能主动让某个用户下线吗? A:标准 JWT 不能(过期前一直有效)。需要即时吊销就用短有效期 + Refresh Token,或加 Redis 黑名单 / 用户令牌版本号。

Q4:前端到底该把 JWT 存哪? A:优先 HttpOnly Cookie(防 XSS),配合 SameSite 和 CSRF 防护;localStorage 易被脚本偷,仅适合低敏场景。

Q5:HS256 密钥要多长才安全? A:至少 256 位(32 字节)且用密码学安全随机数生成,绝不硬编码或用人能猜到的字符串。

十、总结

  • JWT = RFC 7519 的三段式令牌:Header.Payload.Signature,全部 Base64URL 编码;
  • 签名保证不可伪造,但不等于加密------Payload 可读,敏感信息别往里塞;
  • 解码 ≠ 验证 ,每次请求先验签、再查 exp/iss/aud
  • 算法固定白名单防 alg:none,HS256 用强密钥,Token 放 HttpOnly Cookie;
  • 需要即时吊销就上 JWT + Redis 混合方案。

记住一句话:JWT 解决的是"身份信息自描述 + 无状态",安全靠的是签名验证和密钥管理,不是因为它"看起来加密了"。

相关推荐
Codelinghu1 小时前
DeepSeek Harness 出来了,干硬件的我琢磨了一下它到底能干啥
后端
eralong1 小时前
Java 面向对象:继承、多态、接口
java·后端
gyx_这个杀手不太冷静2 小时前
高级前端开发职业规划(2026—2035)
前端·面试·agent
苏三说技术2 小时前
DeepSeek Harness必装的10个插件
后端
阿弱2 小时前
graph-core 策略合并机制的设计与用法
后端·agent
fatcoder2 小时前
玩转Docker 08 — 实战:容器化真实后端并编排
后端·docker·容器
神奇小汤圆2 小时前
从原始的CRUD 到高并发架构:关于秒杀系统的问题拆解与推演
后端
魔兽大山哥2 小时前
NL2SQL 最怕越权:我在执行层叠了好几层 SQL 闸门
后端
foggyprojects2 小时前
当 AI 输出销售额时,如何让它解释这个数字是怎么算出来的?
后端