📝 博客 · 2026-07-29 · ⏱ 10 分钟

JWT是什么?图文详解

用通俗语言讲清楚 JWT 的三段结构、签名验证机制,以及和 Session 的区别与安全风险。

一、JWT 是什么

JWT(JSON Web Token)是一种用 JSON 格式传递认证信息的开放标准(RFC 7519)。简单说,它就是一段带签名的字符串,里面装着"你是谁、你能做什么、什么时候过期"这些信息。

一个典型的 JWT 长这样:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

看起来像三段乱码用 . 连起来——没错,JWT 就是由三部分组成的:

Header.Payload.Signature

JWT 最常见的使用场景是用户登录后的身份认证:用户登录成功后,服务器返回一个 JWT,客户端把它存起来,之后每次请求都带上,服务器通过验证 JWT 知道你是谁。

二、JWT 的三段结构

第一段:Header(头部)

Header 是一个 JSON 对象,描述 token 的类型和签名算法:

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

这个 JSON 会被 Base64URL 编码,变成 JWT 的第一段:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

第二段:Payload(载荷)

Payload 也是 JSON,装着实际要传递的数据,这些数据叫"声明"(Claims)。声明分三类:

1. 标准声明(RFC 7519 规定的)

字段含义
iss签发人(issuer)
sub主题(subject,通常是用户 ID)
aud接收方(audience)
exp过期时间(expiration)
nbf生效时间(not before)
iat签发时间(issued at)
jtiJWT 唯一 ID

2. 私有声明(业务自定义的)

{
  "sub": "1234567890",
  "name": "John Doe",
  "role": "admin",
  "iat": 1516239022,
  "exp": 1516242622
}

这个 JSON 也会被 Base64URL 编码,变成第二段:

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ

注意:Payload 只是 Base64 编码,不是加密。任何人都能解码看到内容,所以千万别在里面放密码、敏感信息。

第三段:Signature(签名)

签名是用来防止篡改的。算法是:

HMACSHA256(
  base64url(header) + "." + base64url(payload),
  secret
)

也就是把前两段用 . 拼起来,用密钥做一次 HMAC-SHA256,得到签名:

SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

把三段用 . 连起来,就是完整的 JWT。

三、Base64URL 编码

JWT 用的是 Base64URL,和普通 Base64 有两点区别:

普通Base64Base64URL
+-
/_
末尾有 =末尾无 =

原因是 JWT 经常放在 URL、HTTP 头里传输,+/= 这些字符会有歧义。

四、签名验证机制

JWT 最核心的安全机制就是签名。验证流程是:

  1. 服务器收到 JWT,按 . 拆成三段
  2. 取前两段(header 和 payload),用同样的算法和密钥重新算一次签名
  3. 把算出来的签名和第三段对比
  4. 如果一致,说明内容没被篡改;不一致则拒绝

关键点:签名用的密钥只有服务器知道。攻击者即使能解出 Payload 里的内容,也修改不了——因为改了之后签名对不上,服务器会拒绝。

举个例子:

五、JWT vs Session

这是登录认证的两大主流方案,对比如下:

维度SessionJWT
存储位置服务器内存/Redis客户端
状态有状态无状态
扩展性需要共享 session 存储天然支持分布式
撤销容易(删 Redis 即可)困劲(需黑名单)
安全性较高(数据在服务端)取决于实现
跨域复杂简单
体积小(cookie 里只存 sessionId)较大(整个 token 都在请求里)

Session 的工作方式

  1. 用户登录,服务器创建一个 session,存到内存或 Redis
  2. 返回一个 sessionId(通过 cookie)
  3. 下次请求带着 cookie,服务器用 sessionId 查到 session 数据
  4. 服务器知道用户是谁

JWT 的工作方式

  1. 用户登录,服务器生成一个 JWT 返回
  2. 客户端把 JWT 存起来(localStorage 或 cookie)
  3. 下次请求把 JWT 放在 Authorization: Bearer xxx 头里
  4. 服务器验证 JWT 签名,直接从 Payload 里读用户信息

六、JWT 的优势

1. 无状态

服务器不需要存 session,每个请求自带身份信息。这意味着:

2. 跨域友好

Cookie 在跨域时会有各种限制(SameSite、CORS),而 JWT 放在 Authorization 头里,不受这些限制影响。前后端分离、移动端、第三方调用都能轻松搞定。

3. 解耦

签发 JWT 的服务和验证 JWT 的服务可以是不同的,只要共享同一个密钥(或用非对称算法的公钥)。微服务架构里特别有用。

七、JWT 的安全风险

JWT 不是银弹,用得不好会有严重的安全问题。

1. XSS 攻击

如果 JWT 存在 localStorage,攻击者通过 XSS 注入脚本就能偷走它。

应对

2. CSRF 攻击

如果 JWT 存在 Cookie 里,攻击者可以构造一个表单,让用户在已登录状态下发起请求。

应对

3. 撤销困难

JWT 一旦签发,在过期前一直有效。如果用户改密码、退出登录、账号被封,旧的 JWT 依然能用。

应对

4. 密钥泄露

签名密钥一旦泄露,攻击者可以伪造任意用户的 JWT。

应对

八、JWT 的实际使用示例

签发 JWT(Node.js)

const jwt = require("jsonwebtoken");

const token = jwt.sign(
  { userId: 123, role: "admin" },
  "my-secret-key",
  { expiresIn: "2h" }
);

验证 JWT

try {
  const payload = jwt.verify(token, "my-secret-key");
  console.log(payload.userId); // 123
} catch (e) {
  console.log("token 无效或已过期");
}

前端发送 JWT

fetch("/api/profile", {
  headers: {
    "Authorization": "Bearer " + token
  }
});

九、实践推荐

调试 API 时经常需要看 JWT 里到底装了什么——尤其是接 OAuth、SSO 这种第三方登录的场景,token 又长又看不懂。在线解码工具是最快的:

粘贴 JWT 后自动解码 Header 和 Payload,高亮显示关键字段(exp、iat、sub),还能检查签名算法和过期时间,是调试鉴权问题时最常用的工具之一。