一、JWT 是什么?(核心定义)
- 全称:JSON Web Token
- 本质 :一种轻量级、自包含的令牌(Token),以 JSON 格式存储用户身份 / 权限等信息,可在客户端和服务端之间安全传输。
- 核心特点 :
- 自包含:Token 本身包含所有认证信息,服务端无需查库 / 缓存即可验证身份(对比 Session:需服务端存储会话信息);
- 跨域 / 跨服务:基于字符串传输,适配前后端分离、微服务跨服务认证场景;
- 可签名:支持对称加密(HMAC)或非对称加密(RSA),防止 Token 被篡改。
二、JWT 的核心结构(三段式,必背)
JWT 由 Header(头部)、Payload(载荷)、Signature(签名) 三部分组成,各部分用 . 分隔,最终格式如:
plaintext
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjEsInVzZXJuYW1lIjoiemhhbmdzYW4iLCJleHAiOjE3MTk4MTU4MDJ9.5Z8n9jG0Q7t2X8kL7s9a8d7f6g5h4j3k2l1m0n9b8v7c6x5s4d3f2g1h0j9k8l7m6n5b4v3c2x1s0a9s8d7f6g5h4j3k2l1
1. Header(头部)
-
作用:描述 JWT 的元数据(加密算法、令牌类型);
-
格式 :JSON 字符串,通常包含两个字段:
alg:使用的签名算法(如HS256(HMAC-SHA256)、RS256(RSA-SHA256));typ:令牌类型,固定为JWT;
-
示例 :
json
{ "alg": "HS256", "typ": "JWT" } -
处理:Base64Url 编码(不是加密,可反向解码),成为 JWT 第一部分。
2. Payload(载荷)
-
作用:存储实际需要传递的信息(用户 ID、用户名、过期时间等);
-
分类 :
-
「注册声明」(推荐使用,预定义字段): 表格
字段名 含义 示例 iss签发者(Token 发行方) https://xxx.comsub主题(面向的用户) 1001(用户 ID)exp过期时间(时间戳) 1719815802(必加)nbf生效时间 1719810000iat签发时间 1719810802jtiJWT 唯一标识 f89s7d6g5h4j3k2l1 -
「公共声明」:自定义字段(如
username、role),但避免存敏感信息; -
「私有声明」:前后端协商的自定义字段(如
userId);
-
-
示例 :
json
{ "userId": 1, "username": "zhangsan", "exp": 1719815802 } -
处理:Base64Url 编码,成为 JWT 第二部分(⚠️ 可解码,绝对不能存密码、token 等敏感信息)。
3. Signature(签名)
-
作用:验证 Token 是否被篡改,保证数据完整性;
-
生成逻辑 :
- 将编码后的 Header +
.+ 编码后的 Payload 拼接成字符串; - 使用 Header 中指定的算法,结合「密钥」对该字符串进行加密;
- 将编码后的 Header +
-
示例(HS256 算法) :
plaintext
HMACSHA256( base64UrlEncode(Header) + "." + base64UrlEncode(Payload), "my-secret-key" // 服务端唯一密钥,绝对不能泄露 ) -
处理:加密结果作为 JWT 第三部分(无法反向解密,仅用于验证)。
三、JWT 的核心工作流程(前后端分离场景)
以「用户登录获取 Token,后续请求携带 Token 认证」为例:
预览
查看代码
服务端客户端服务端客户端提交用户名+密码(登录请求)验证用户名密码是否正确验证通过,生成 JWT(包含用户ID、过期时间)返回 JWT(如:响应体中 `token: "eyJhbGciOiJIUzI1Ni..."`)存储 JWT(LocalStorage/SessionStorage/Cookie)发起后续请求(如:获取用户信息),请求头携带 JWT(Authorization: Bearer {token})验证 JWT 签名+过期时间,解析出用户ID返回请求结果(无需再次验证密码)
sequenceDiagram
participant 客户端
participant 服务端
客户端->>服务端: 提交用户名+密码(登录请求)
服务端->>服务端: 验证用户名密码是否正确
服务端->>服务端: 验证通过,生成 JWT(包含用户ID、过期时间)
服务端->>客户端: 返回 JWT(如:响应体中 `token: "eyJhbGciOiJIUzI1Ni..."`)
客户端->>客户端: 存储 JWT(LocalStorage/SessionStorage/Cookie)
客户端->>服务端: 发起后续请求(如:获取用户信息),请求头携带 JWT(Authorization: Bearer {token})
服务端->>服务端: 验证 JWT 签名+过期时间,解析出用户ID
服务端->>客户端: 返回请求结果(无需再次验证密码)
服务端客户端服务端客户端提交用户名+密码(登录请求)验证用户名密码是否正确验证通过,生成 JWT(包含用户ID、过期时间)返回 JWT(如:响应体中 `token: "eyJhbGciOiJIUzI1Ni..."`)存储 JWT(LocalStorage/SessionStorage/Cookie)发起后续请求(如:获取用户信息),请求头携带 JWT(Authorization: Bearer {token})验证 JWT 签名+过期时间,解析出用户ID返回请求结果(无需再次验证密码)
关键步骤说明:
- Token 生成:登录成功后,服务端根据用户信息 + 密钥生成 JWT,返回给客户端;
- Token 存储:客户端将 JWT 存在 LocalStorage(跨域友好)或 Cookie(更安全);
- Token 携带 :后续请求通过
Authorization请求头携带(规范格式:Bearer + 空格 + JWT); - Token 验证 :服务端收到 Token 后:
- 拆分 Header/Payload/Signature;
- 用相同密钥 + 算法验证签名(防止篡改);
- 检查
exp字段(防止 Token 过期); - 验证通过则解析 Payload 中的用户信息,处理请求。
四、JWT 的核心使用场景
表格
| 场景 | 优势 |
|---|---|
| 前后端分离认证 | 无需服务端存储 Session,跨域友好 |
| 微服务跨服务授权 | 服务间传递身份信息,无需重复认证 |
| 一次性验证(如邮件激活) | 可设置短过期时间,结合 jti 防止重复使用 |
| 移动端 API 认证 | 轻量级字符串,适配移动端网络传输 |
五、JWT 的优缺点(面试高频)
优点
- 无状态:服务端无需存储 Token,减轻服务器压力(对比 Session:需存储会话,分布式场景需 Redis 共享);
- 跨域 / 跨服务:Token 是字符串,可在任意平台传递,适配前后端分离、微服务;
- 自包含:Payload 可存储基础用户信息,减少服务端查库次数;
- 易扩展:支持多种加密算法,可按需选择对称 / 非对称加密。
缺点
- 无法主动吊销:Token 生成后,除非过期,否则无法主动作废(如用户退出登录,服务端仍需验证 Token,解决方案:结合 Redis 维护黑名单);
- Payload 可解码:Base64Url 编码不是加密,Payload 中的信息可被解码,绝对不能存敏感信息(如密码、银行卡号);
- Token 可能过长:如果 Payload 存储过多信息,Token 字符串会变长,增加网络传输开销;
- 过期时间固定:无法动态修改 Token 过期时间(需重新生成 Token)。
六、JWT 实战关键注意事项(避坑指南)
1. 安全相关(重中之重)
- 绝不存敏感信息:Payload 仅存用户 ID、用户名、角色等非敏感信息,密码、token 等绝对不能存;
- 密钥绝对保密:对称加密的密钥(如 HS256 的密钥)需妥善保管,泄露则攻击者可伪造 Token;
- 使用 HTTPS 传输:防止 Token 在网络传输中被劫持;
- 设置合理过期时间:短期过期(如 2 小时),结合「刷新令牌(Refresh Token)」实现无感续期;
- 避免用 Base64 编码混淆敏感信息:Base64Url 可轻松解码,不要误以为是加密。
2. 续期方案(解决 Token 过期问题)
- 双 Token 机制 :
- Access Token:短期有效(2 小时),用于接口认证;
- Refresh Token:长期有效(7 天),用于 Access Token 过期后,获取新的 Access Token;
- 优势:无需用户重新登录,实现无感续期。
3. 吊销 Token(解决 "无法主动作废" 问题)
- 结合 Redis 维护「Token 黑名单」:
- 用户退出登录 / 账号异常时,将 Token 存入 Redis(设置与 Token 相同的过期时间);
- 服务端验证 Token 时,先检查 Redis 黑名单,存在则拒绝请求。
4. 技术选型(Java 生态)
-
推荐依赖:
jjwt(Java JWT 工具包,简单易用); -
核心代码示例(生成 / 验证 Token): java
运行
import io.jsonwebtoken.Jwts; import io.jsonwebtoken.security.Keys; import javax.crypto.SecretKey; import java.util.Date; public class JwtUtil { // 密钥(建议至少 256 位,避免弱密钥) private static final SecretKey SECRET_KEY = Keys.hmacShaKeyFor("my-secret-key-12345678901234567890".getBytes()); // 过期时间:2 小时 private static final long EXPIRATION = 2 * 60 * 60 * 1000; // 生成 Token public static String generateToken(Long userId, String username) { return Jwts.builder() .claim("userId", userId) // 自定义字段 .claim("username", username) .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION)) // 过期时间 .signWith(SECRET_KEY) // 签名 .compact(); } // 验证 Token 并解析用户ID public static Long getUserIdFromToken(String token) { return Jwts.parserBuilder() .setSigningKey(SECRET_KEY) .build() .parseClaimsJws(token) // 验证签名,失败则抛异常 .getBody() .get("userId", Long.class); } }
七、JWT vs Session(面试对比)
表格
| 特性 | JWT | Session |
|---|---|---|
| 存储位置 | 客户端(LocalStorage/Cookie) | 服务端(内存 / Redis) |
| 状态 | 无状态 | 有状态 |
| 跨域 | 友好(字符串传输) | 不友好(Cookie 跨域限制) |
| 分布式部署 | 无需额外配置 | 需 Redis 共享 Session |
| 主动吊销 | 困难(需结合黑名单) | 简单(直接删除服务端 Session) |
| 数据大小 | 可自定义(不宜过大) | 服务端存储,无传输大小限制 |
总结
- 核心结构:JWT = Header(Base64Url) + Payload(Base64Url) + Signature(加密),三段式字符串;
- 核心流程:登录生成 Token → 客户端存储 → 请求携带 Token → 服务端验证签名 + 过期时间;
- 核心注意:不存敏感信息、密钥保密、设置短过期时间、结合 Redis 解决吊销问题;
- 核心优势:无状态、跨域友好,适配前后端分离 / 微服务架构;
- 核心缺点:无法主动吊销、Payload 可解码,需结合额外方案弥补。