在现代 Web 应用和微服务架构中,用户身份认证是系统安全的第一道防线。传统的 Session 认证在分布式系统中面临共享难题,而 JWT(JSON Web Token) 凭借其无状态、自包含、跨域友好的特性,成为当前最主流的身份认证方案之一。
本文将带你深入理解 JWT 的核心原理,并通过 Spring Boot 3.x + JJWT 实现令牌的生成、解析、验证与实际应用。
JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在各方之间以 JSON 对象的形式安全地传输信息。它通常用于:
✅ JWT 的核心优势:
一个 JWT 令牌由三部分组成,用 . 分隔:
xxxxx.yyyyy.zzzzz包含令牌类型和签名算法:
{
"alg": "HS256",
"typ": "JWT"
}alg:签名算法,如 HS256(HMAC SHA-256)、RS256(RSA)typ:令牌类型,固定为 JWT编码后:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
包含“声明(Claims)”——关于用户和附加信息的 JSON 数据。
类型 | 说明 | 示例 |
|---|---|---|
Registered Claims | 预定义标准字段(非强制) | exp(过期时间)、iss(发行人)、sub(主题)、aud(受众) |
Public Claims | 公共字段,建议使用命名空间 | https://example.com/role |
Private Claims | 自定义私有字段 | userId, username, role |
示例:
{
"id": 1,
"username": "KBL",
"exp": 1727779154,
"iss": "myapp"
}编码后:eyJpZCI6MSwidXNlcm5hbWUiOiJLQkwiLCJleHAiOjE3Mjc3NzkxNTR9
⚠️ 注意:
用于验证令牌完整性和防止篡改。
生成方式:
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secretKey
)签名后:7Nt1IbfAZ52PKW5cjjbjr3oND9iaJxvGFO5w5g3uNV8
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJpZCI6MSwidXNlcm5hbWUiOiJLQkwiLCJleHAiOjE3Mjc3NzkxNTR9.
7Nt1IbfAZ52PKW5cjjbjr3oND9iaJxvGFO5w5g3uNV8
环境 | 版本 |
|---|---|
操作系统 | Windows 10 |
IDE | IntelliJ IDEA |
JDK | 17 |
构建工具 | Maven 3.6.0+ |
Spring Boot | 3.x |
JWT 库 | JJWT 0.12.3 |
✅ 全程需联网下载依赖。
在 pom.xml 中添加:
<!-- JWT 依赖 -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.12.3</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.12.3</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.12.3</version>
<scope>runtime</scope>
</dependency>💡 注意:JJWT 0.12+ 版本已模块化,需引入
api、impl、jackson三个模块。
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.junit.jupiter.api.Test;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class JwtTest {
// 密钥(应配置在 application.yml 中,此处仅为演示)
private static final String SECRET_KEY = "chengxuyuanchengxuyuanchengxuyuanchengxuyuanchengxuyuan";
/**
* 生成 JWT 令牌
*/
@Test
public void generateJwt() {
// 自定义负载(Claims)
Map<String, Object> claims = new HashMap<>();
claims.put("id", 1);
claims.put("username", "KBL");
// 生成令牌
String jwt = Jwts.builder()
.setClaims(claims) // 设置自定义内容
.setExpiration(new Date(System.currentTimeMillis() + 24 * 3600 * 1000)) // 24小时有效期
.signWith(SignatureAlgorithm.HS256, SECRET_KEY) // 使用 HS256 算法签名
.compact();
System.out.println("生成的 JWT 令牌:");
System.out.println(jwt);
}
}✅ 输出示例: eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwidXNlcm5hbWUiOiJLQkwiLCJleHAiOjE3Mjc3NzkxNTR9.7Nt1IbfAZ52PKW5cjjbjr3oND9iaJxvGFO5w5g3uNV8
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import org.junit.jupiter.api.Test;
public class JwtTest {
private static final String SECRET_KEY = "chengxuyuanchengxuyuanchengxuyuanchengxuyuanchengxuyuan";
/**
* 解析 JWT 令牌
*/
@Test
public void parseJwt() {
String jwt = "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwidXNlcm5hbWUiOiJLQkwiLCJleHAiOjE3Mjc3NzkxNTR9.7Nt1IbfAZ52PKW5cjjbjr3oND9iaJxvGFO5w5g3uNV8";
try {
Claims claims = Jwts.parser()
.setSigningKey(SECRET_KEY) // 必须与生成时密钥一致
.build()
.parseClaimsJws(jwt) // 解析并验证签名
.getBody();
System.out.println("解析成功,Claims 内容:");
System.out.println("用户ID: " + claims.get("id"));
System.out.println("用户名: " + claims.get("username"));
System.out.println("过期时间: " + claims.getExpiration());
} catch (Exception e) {
System.err.println("JWT 解析失败:" + e.getMessage());
// 可能原因:签名无效、令牌过期、密钥错误
}
}
}⚠️ 异常处理非常重要!常见异常:
ExpiredJwtException:令牌已过期SignatureException:签名不匹配(可能被篡改)MalformedJwtException:格式错误SECRET_KEY)✅ 适用于调试、教学、快速验证。
项目 | 建议 |
|---|---|
密钥管理 | 使用强密钥(至少 32 字符),不要硬编码,应配置在 application.yml 或环境变量中 |
传输安全 | 必须使用 HTTPS 传输 JWT,防止中间人攻击 |
存储位置 | 前端推荐使用 HttpOnly Cookie 存储,避免 XSS 攻击;若用 localStorage,需防范 XSS |
过期时间 | 设置合理过期时间(如 2h),配合 Refresh Token 实现自动续期 |
敏感信息 | 不要在 JWT 中存放密码、手机号等敏感信息 |
123456)在真实项目中,JWT 通常结合 拦截器(Interceptor) 或 过滤器(Filter) 使用:
@Component
public class JwtAuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String token = request.getHeader("Authorization");
if (token != null && token.startsWith("Bearer ")) {
String jwt = token.substring(7);
try {
Claims claims = Jwts.parser().setSigningKey(SECRET_KEY).build().parseClaimsJws(jwt).getBody();
// 将用户信息存入 ThreadLocal 或 SecurityContext
return true;
} catch (Exception e) {
response.setStatus(401);
return false;
}
}
response.setStatus(401);
return false;
}
}🚀 进阶建议:结合 Spring Security 实现完整的认证授权体系。
问题 | 解决方案 |
|---|---|
无法主动失效(除非过期) | 使用 黑名单机制(Redis 存储已注销的 JWT) |
令牌较大 | 避免存放过多自定义字段 |
一旦泄露风险高 | 缩短有效期,使用 Refresh Token |
JWT 是现代 Web 开发中不可或缺的身份认证技术。它简单、高效、跨平台,特别适合前后端分离和微服务架构。
但记住:JWT 不是银弹。它解决了 Session 共享问题,也带来了令牌管理、安全防护等新挑战。
掌握 JWT 的核心原理与安全实践,是每一位 Java 开发者迈向高阶的必经之路。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。