JWT 是什么?JSON Web Token 完整指南
JWT(JSON Web Token)是目前最流行的无状态鉴权方案之一,被广泛用于前后端分离、微服务、移动端 API 鉴权场景。它本质上是一个由三段 Base64URL 编码字符串拼接而成的紧凑型令牌,里面携带了经过签名的"声明(claims)"。本文将带你彻底搞懂 JWT 的结构、原理、常见算法对比、与传统 Session 的区别,以及生产环境必须避开的 4 个安全坑。
一、JWT 的三段式结构
一个 JWT 长这样:header.payload.signature,每段用英文句点 . 分隔。它遵循 RFC 7519 标准,下面分别拆解。
1. Header(头部)
声明算法和令牌类型,例如:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gIiJpYXQiOjE1MTYyMzkwMjJ9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
"alg": "HS256",
"typ": "JWT"
}
上面这段 JSON 会被 Base64URL 编码(注意是 URL 安全的 Base64,不是普通 Base64,区别见 Base64 编码完全指南),得到:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gIiJpYXQiOjE1MTYyMzkwMjJ9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
2. Payload(负载)
存放实际要传递的"声明"(claims),分为三类:
- Registered claims(标准声明):官方预定义字段,建议但非强制。
iss(issuer)— 签发者sub(subject)— 主题(通常是用户 ID)aud(audience)— 接收方exp(expiration time)— 过期时间戳iat(issued at)— 签发时间戳nbf(not before)— 生效时间jti(JWT ID)— 唯一标识,可用于防重放
- Public claims(公共声明):自定义字段,需在 IANA JSON Web Token Registry 注册或使用 URI 命名空间,避免冲突。
- Private claims(私有声明):服务端与客户端双方约定的自定义字段(如
user_id、role)。
3. Signature(签名)
用于验证 token 在传输中未被篡改。以 HS256 为例:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gIiJpYXQiOjE1MTYyMzkwMjJ9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)
签名是整个 JWT 防伪的核心。服务端收到 token 后,用同样的算法和密钥重算签名,对比是否一致。
二、HS256 vs RS256 vs ES256 怎么选?
JWT 的 alg 字段决定签名算法,三种最常见:
| 算法 | 类型 | 密钥 | 适用场景 |
|---|---|---|---|
| HS256 | 对称 HMAC | 1 个 secret | 单体应用、内部服务 |
| RS256 | 非对称 RSA | 私钥签 + 公钥验 | 多服务验证、OAuth 2.0、OIDC |
| ES256 | 非对称 椭圆曲线 | 私钥签 + 公钥验 | 移动端、短签名、高安全 |
选型建议:单体后端用 HS256(简单高效);涉及多个下游服务需要验证 token 时用 RS256(公钥可公开分发,避免密钥泄露);对性能和包大小敏感(移动端 IoT)选 ES256,签名更短、安全性更高。
三、完整示例(来自 jwt.io,可解码验证)
下面这个 token 来自 jwt.io 官方文档,结构完整,可以直接粘到任意 JWT 解码器验证:
Header(原文)
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gIiJpYXQiOjE1MTYyMzkwMjJ9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
"alg": "HS256",
"typ": "JWT"
}
Base64URL 编码后:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gIiJpYXQiOjE1MTYyMzkwMjJ9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Payload(原文)
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gIiJpYXQiOjE1MTYyMzkwMjJ9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
"sub": "1234567890",
"name": "John",
"iat": 1516239022
}
Base64URL 编码后:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gIiJpYXQiOjE1MTYyMzkwMjJ9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
完整 JWT(三段拼接)
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gIiJpYXQiOjE1MTYyMzkwMjJ9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
把这段粘到 JWT 解码工具,Header / Payload / Signature 即可一目了然。
四、JWT vs Session Cookie 核心对比
| 维度 | JWT | Session Cookie |
|---|---|---|
| 状态 | 无状态(stateless) | 有状态(服务端存 session) |
| 扩展性 | 天然支持分布式 | 需共享存储(Redis 等) |
| 撤销 | 较难(需黑名单或短 exp) | 简单(删服务端记录) |
| 体积 | 较大(每次请求都带) | 轻量(只带 cookie ID) |
| CSRF | 免疫(用 Authorization header) | 需 CSRF token |
一句话总结:JWT 赢在扩展性和跨域,Session 赢在撤销和体积。现代系统常用"JWT access token + refresh token"组合来取长补短。
五、实战:用户登录鉴权流程
┌──────────┐ ┌──────────┐ ┌──────────┐
│ 浏览器 │ │ 鉴权服务 │ │ 业务API │
└────┬─────┘ └────┬─────┘ └────┬─────┘
│ ① POST /login (user, pwd) │ │
│ ────────────────────────────────────▶│ │
│ │ ② 校验账号密码 │
│ │ 生成 JWT(HS256, exp=2h) │
│ ③ 返回 { token: "eyJhbG..." } │ │
│ ◀────────────────────────────────────│ │
│ │ │
│ ④ GET /api/user │
│ Header: Authorization: Bearer eyJhbG... │
│ ─────────────────────────────────────────────────────────────────────────▶│
│ │ │ ⑤ 取出 token
│ │ │ 验签 + 检查 exp
│ │ │ 从 sub 取 user_id
│ ⑥ 200 OK { name: "John" } │
│ ◀─────────────────────────────────────────────────────────────────────────│
│ │ │
│ ⑦ 2h 后 exp 过期,返回 401 │
│ 客户端用 refresh token 换新 access token │
│ ⑧ POST /refresh (refresh_token) │
│ ────────────────────────────────────▶│ │
│ ⑨ 返回新 access token │ │
│ ◀────────────────────────────────────│ │
└──────────┘ └──────────┘ └──────────┘
六、生产环境 4 个真实安全风险
任意 XSS 漏洞都能
localStorage.getItem('token') 拿到 token。推荐用 httpOnly + Secure + SameSite=Strict 的 cookie 存放,JS 根本无法读取,从源头杜绝 XSS 窃取。
历史上出现过
{"alg":"none"} 攻击(攻击者去掉签名强行通过)和算法替换攻击(用公钥当 HMAC 密钥伪造 RS256 token)。服务端必须写死期望算法,不要相信 header 里的 alg。
JWT 在网络上明文传输等于把用户凭证裸奔。一旦被中间人截获,攻击者就能在 token 有效期内冒充用户。HTTPS(含 HSTS)是底线,没有例外。
access token 过期时间建议 15 分钟 ~ 2 小时,搭配长寿命的 refresh token(存服务端、可主动撤销)。这样即便 access token 泄露,损失也被控制在小窗口内。
七、3 步在线解码你的 JWT
- 打开 devstoolbox.net/tools/jwt-decoder.html
- 把 JWT 字符串粘到输入框(Header.Payload.Signature)
- 即时看到 header / payload 解码结果,全程浏览器本地解码,不会上传到任何服务器,放心用
💡 如果你处理的是带 ? 或特殊字符的 URL,参考 URL 编码完全指南。
八、常见问题 FAQ
Q1: JWT 能存敏感数据吗?
绝对不能。Payload 只是 Base64URL 编码,任何拿到 token 的人都能直接解码看到原文,等同于明文。密码、身份证、手机号、银行卡等信息绝对不能放进 payload。
Q2: JWT 怎么撤销?
三种思路:① 短 exp + refresh token(最常用);② 黑名单(把要撤销的 jti 存 Redis,校验前先查);③ 维护一个 版本号 / 密钥轮换 机制强制旧 token 全部失效。
Q3: JWT 和 OAuth 是什么关系?
两者不是同一层概念。OAuth 2.0 是一种授权框架(authorization framework),解决"第三方应用怎么拿到用户授权"的问题;JWT 是一种 token 格式。OAuth 2.0 实际跑起来时,access token 的载体经常就是 JWT(即所谓的 "JWT Bearer Token")。
Q4: JWT 适合做 API 鉴权吗?
非常适合,特别是 stateless REST API 和微服务。但前提是严格遵守上面的 4 个安全实践:短 exp、HTTPS、显式校验 alg、妥善存放 token。
Q5: 各语言怎么选 JWT 库?
- Node.js:
jsonwebtoken(社区标杆) - Python:
PyJWT - Java:
jjwt(io.jsonwebtoken) - Go:
github.com/golang-jwt/jwt
九、总结
JWT 不是银弹,但它凭借无状态、跨域、自包含的特性,在现代分布式架构中几乎成了鉴权的事实标准。掌握它的三段式结构、算法选择、与 Session 的差异、以及 4 个安全红线,你就能在面试和生产中游刃有余。
想快速验证一个 JWT 的内容?直接用 DevToolbox JWT 解码器,粘贴即得结果,纯本地处理,绝不上传。
📚 相关文章:Base64 编码完全指南 · URL 编码完全指南 · 在线 JWT 解码工具