大家好,今天我们来聊聊如何在 Spring Boot 中集成 JWT(JSON Web Token)。我会从最基础的配置讲起,一步步带你理解 JWT 的加密方法、生成过程和验证方式。我们会从一个简单的实现开始,发现其中的问题,然后逐步优化,逼近如今最主流的方案。废话不多说,直接开整!
1. Spring Boot 集成 JWT 需要的 POM 依赖
要在 Spring Boot 项目中使用 JWT,我们得先把必要的依赖加到 pom.xml
里。JWT 是个用来安全传输信息的标准,特别适合做身份验证。下面是需要的依赖:
xml
<!-- JWT 核心 API -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<!-- JWT 实现 -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<!-- JSON 处理 -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
这些依赖是干嘛的?
jjwt-api
:提供 JWT 的核心接口,比如生成和解析 token。jjwt-impl
:具体的实现逻辑,runtime 作用域表示运行时才需要。jjwt-jackson
:处理 JSON,因为 JWT 的数据部分是 JSON 格式的,靠它来序列化和反序列化。
有了这些依赖,我们就可以开始玩转 JWT 了!
2. JWT 的加密方法:有哪些?怎么选?
JWT 的安全性主要靠签名,签名用到的加密方法直接决定了 token 的可靠程度。常见的加密方法有这么几种:
常见的加密方法
-
HS256(HMAC with SHA-256)
- 类型:对称加密
- 特点:用同一个密钥签名和验证,简单高效。
- 适用场景:单服务系统,密钥管理不复杂。
-
RS256(RSA with SHA-256)
- 类型:非对称加密
- 特点:私钥签名,公钥验证,安全性高。
- 适用场景:分布式系统,公钥可以公开分享。
-
ES256(ECDSA with SHA-256)
- 类型:非对称加密
- 特点:用椭圆曲线算法,密钥短,签名快。
- 适用场景:移动设备或资源受限的场景。
比较一下它们的异同
算法 | 类型 | 密钥管理 | 安全性 | 性能 | 用在哪儿 |
---|---|---|---|---|---|
HS256 | 对称 | 一个密钥搞定 | 中等 | 快 | 单服务、小项目 |
RS256 | 非对称 | 私钥签,公钥验 | 高 | 中等 | 分布式、公开 API |
ES256 | 非对称 | 私钥签,公钥验 | 高 | 快 | 移动端、IoT设备 |
举个例子帮你记住
想象你是个店老板,要给顾客发"VIP凭证":
- HS256:你用一个秘密口令写凭证,顾客拿回来你再用这个口令验。简单,但口令得藏好,不能泄露。
- RS256:你用私人印章盖凭证,顾客拿回来你给个公开的"印章模板"就能验。印章丢不了,别人还能帮你验。
- ES256:类似 RS256,但你的印章更小巧,盖章更快,适合给手机用户发凭证。
3. 生成 JWT:结构和代码
JWT 长啥样?它由三部分组成,用点号分隔:
css
Header.Payload.Signature
JWT 的通用结构
-
Header :描述 token 的类型和加密算法,比如:
json{ "alg": "HS256", "typ": "JWT" }
-
Payload :放实际数据(声明),比如用户信息:
json{ "sub": "10086", "name": "小明", "iat": 1698765432 }
-
Signature:用密钥对 Header 和 Payload 签名,确保不被篡改。
这三部分会分别被 Base64 编码,然后用点连起来,最后加上签名。
生成 JWT 的代码(用 HS256)
下面用 Java 代码生成一个 token:
java
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import java.security.Key;
import java.util.Date;
public class JwtDemo {
public static void main(String[] args) {
// 生成一个 HS256 的密钥
Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256);
// 生成 JWT
String jwt = Jwts.builder()
.setHeaderParam("typ", "JWT") // Header
.setSubject("10086") // Payload: 用户ID
.setClaim("name", "小明") // Payload: 自定义声明
.setIssuedAt(new Date()) // Payload: 发行时间
.signWith(key) // Signature: 用密钥签名
.compact(); // 合成 token
System.out.println("生成的 JWT: " + jwt);
}
}
运行后,你会得到一个长字符串,比如:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMDA4NiIsIm5hbWUiOiLlsbPniYgiLCJpYXQiOjE2OTg3NjU0MzJ9.一串签名
4. 验证 JWT:怎么解密和检查?
验证 JWT 其实不涉及"解密",而是检查签名和声明。签名对了,token 就是真的;声明没问题(比如没过期),就能用。
验证步骤
- 解析 Header:看看用的是啥算法。
- 检查签名:用密钥验证签名是否匹配。
- 检查 Payload:确认声明,比如时间是否过期。
验证代码(用 HS256)
接着上面的例子,验证一下:
java
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
public class JwtDemo {
public static void main(String[] args) {
Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256);
String jwt = "刚刚生成的那个 token";
try {
Claims claims = Jwts.parserBuilder()
.setSigningKey(key) // 设置密钥
.build()
.parseClaimsJws(jwt) // 解析 token
.getBody(); // 拿到 Payload
System.out.println("用户ID: " + claims.getSubject());
System.out.println("用户名: " + claims.get("name"));
} catch (Exception e) {
System.out.println("token 不合法!");
}
}
}
用到的核心 API 是 Jwts.parserBuilder()
和 parseClaimsJws()
,它们帮你把 token 拆开并验证签名。
从简单到复杂:优化 JWT 的使用
朴素方案:HS256 单密钥
我们先从最简单的 HS256 开始:
- 服务器用一个密钥生成 token。
- 客户端拿着 token 访问,服务器用同一个密钥验证。
问题来了
- 密钥咋管? 单服务还好说,但如果有多个服务,得把密钥传过去,容易泄露。
- 安全性咋办? 密钥丢了,别人就能伪造 token。
优化方向
- 换非对称加密:用 RS256,私钥签名,公钥验证,公钥随便发,不怕泄露。
- 分布式友好:多个服务都能用公钥验 token,不用共享私钥。
主流方案:RS256 非对称加密
改用 RS256:
- 服务器用私钥生成 token。
- 客户端访问时,服务器或其他服务用公钥验证。
好处
- 安全性提升,私钥只留服务器。
- 分布式系统里,公钥随便分发,验证方便。
代码调整
生成 token 时用 RSA 密钥对:
java
import java.security.KeyPair;
import io.jsonwebtoken.security.Keys;
KeyPair keyPair = Keys.keyPairFor(SignatureAlgorithm.RS256);
String jwt = Jwts.builder()
.setSubject("10086")
.signWith(keyPair.getPrivate())
.compact();
验证时用公钥:
java
Claims claims = Jwts.parserBuilder()
.setSigningKey(keyPair.getPublic())
.build()
.parseClaimsJws(jwt)
.getBody();
再进一步:解决 token 不可撤销
RS256 虽然安全,但 JWT 一旦发出,除非过期,否则没法主动废掉。如果 token 被偷了,麻烦就大了。
优化方案
- 短命 access token + 长命 refresh token:access token 设个短过期时间(比如 15 分钟),过期后用 refresh token 换新的。
- 黑名单机制:用户退出或 token 被盗时,把 refresh token 加到黑名单。
主流做法
- access token:有效期短,负责日常认证。
- refresh token:有效期长,负责续命。
- 黑名单:用 Redis 存被废掉的 token,验证时查一下。
总结
在 Spring Boot 里集成 JWT,核心是加好依赖(jjwt-api
等),选对加密方法(HS256、RS256、ES256),然后用代码生成和验证 token。从最简单的 HS256 开始,我们发现密钥管理的问题,进化到 RS256 的非对称加密,适应了分布式需求。再加上 refresh token 和黑名单,就成了现在最靠谱的方案。
希望这篇博客能让你对 JWT 从入门到精通,赶紧动手试试吧!有什么问题,欢迎留言聊聊!