JWT实现-jjwt

介绍

JJWT(Java JWT)是 Java 生态里最主流、企业用得最多的 JWT 工具库,专门用来在后端(SpringBoot/SpringCloud 等)生成、签名、解析、验证 JWT 令牌,做登录鉴权、接口授权、微服务间身份传递。


一、JJWT 是什么

  • 全称:Java JWT(Java JSON Web Token)
  • 定位:纯 Java 实现的 JWT 开源库,完全遵循 RFC 7519(JWT)、RFC 7515(JWS)、RFC 7516(JWE) 等规范
  • 适用:SpringBoot 2/3、微服务、单体应用、Android 等 JVM 环境
  • 特点:API 简洁、链式调用、安全校验强、异常清晰

二、核心能力(你代码里用到的就是这些)

1. 生成 JWT(签发)

  • 存放用户信息:claims(自定义数据)、subject(用户名)
  • 标准字段:签发时间 issuedAt、过期时间 expiration
  • 签名防篡改:signWith(密钥)(HMAC/RSA/ECDSA)
  • 最终生成:compact() → 一段 xxx.yyy.zzz 字符串

2. 解析与验证(你代码里的 parser)

  • 验签:verifyWith(密钥) → 确保没被篡改
  • 自动校验:过期、签名错误、格式非法 直接抛异常
  • 取数据:parseSignedClaims(token).getPayload() → 拿到用户信息

3. 支持的算法(企业常用)

  • 对称签名(简单安全):HS256 / HS384 / HS512(你现在用的就是这类)
  • 非对称(更高安全):RS256 / ES256 / PS256(适合微服务/公钥分发)

三、版本与依赖

1. 主流版本(企业最稳)

  • 0.9.x:老版,API 不同(已淘汰)
  • 0.11.x:过渡版
  • 0.12.x:当前企业主流、最推荐

2. 0.12.x 关键变化(和你代码直接相关)

  • 新 API:
    • 签名:signWith(SecretKey)(不再传算法)
    • 解析:verifyWith(SecretKey).build().parseSignedClaims()
  • 强制安全:
    • HMAC 密钥 必须 ≥32 字节(你代码里 secret 要够长)
    • 自动拒绝弱算法、弱密钥

版本选择

一、企业存量项目(最多、最稳):0.11.5

  • 现状:80% 以上老项目、Spring Boot 2.x 主流在用
  • 特点:
    • 稳定、无坑、文档/案例最多
    • 兼容 JDK 8 ~ 17、Spring Boot 2.x
    • 依赖拆分:jjwt-api + jjwt-impl + jjwt-jackson
    • API:parserBuilder()、signWith(key, alg) 风格
  • 为什么流行:
    • 0.11.x 是 长期稳定分支,修复了大量安全漏洞(含 CVE 防护)
    • 很多公司技术栈锁在 Spring Boot 2.7.x / JDK 11,配套就是 0.11.5

二、新项目 / Spring Boot 3 标配:0.12.3 ~ 0.12.5

  • 你现在用的 0.12.5 就是这个阵营的最新稳定版
  • 特点:
    • 原生支持 Spring Boot 3 / Jakarta EE(jakarta.servlet)
    • 简化依赖:单 jar jjwt 包含全部(api+impl+jackson)
    • API 更简洁:Jwts.builder().signWith(key)、parser().verifyWith(key).build()
    • 更强安全校验(拒绝伪签名、不合规 JWT)
  • 适用:
    • Spring Boot 3.0+、JDK 17+ 新项目必选
    • 你现在的版本 完全正确、企业主流、可直接上生产

三、过时版本(不要再用)

  • 0.9.1 及更早:
    • 非常老、API 不兼容、不支持 Spring Boot 3
    • 方法:setSigningKey(String)、parser().setSigningKey() 已废弃

springboot3 实现 JWT

提醒

SpringBoot3 必须用 JJWT 0.12.3+ 版本!
旧版 JJWT(0.9.1)不支持 SpringBoot3(Jakarta),会直接报错!

一、pom.xml 依赖(直接复制)

<!-- JJWT 最新版(SpringBoot3 专用) -->
<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>

二、application.yml 配置

# JWT 配置
jwt:
  secret: yourSecretKeyyourSecretKeyyourSecretKeyyourSecretKey # 密钥要足够长
  expire: 86400000 # 过期时间 1天

三、JWT 工具类

JwtUtil.java

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.crypto.SecretKey;
import java.util.Date;
import java.util.Map;

/**
 * JWT工具类
 * 用于生成Token、解析Token、校验Token、从Token中获取用户信息
 * 适配 jjwt 0.12.3+ 版本 & SpringBoot3
 */
@Component
public class JwtUtil {

    /**
     * JWT 密钥
     * 从配置文件读取,长度必须 >=32 位,用于签名加密
     */
    @Value("${jwt.secret}")
    private String secret;

    /**
     * Token 过期时间
     * 从配置文件读取,单位:毫秒
     */
    @Value("${jwt.expire}")
    private Long expire;

    /**
     * 获取加密密钥
     * 将配置的字符串密钥转为 JJWT 需要的 SecretKey 对象
     */
    private SecretKey getSecretKey() {
        return Keys.hmacShaKeyFor(secret.getBytes());
    }

    /**
     * 生成 JWT Token
     * @param claims 自定义载荷(存放用户ID、角色等信息)
     * @param username 用户名(作为Token的唯一标识)
     * @return 生成好的 Token 字符串
     */
    public String generateToken(Map<String, Object> claims, String username) {
        return Jwts.builder()
                .claims(claims)          // 设置自定义载荷(存放额外信息)
                .subject(username)       // 设置主题(一般放用户名/账号)
                .issuedAt(new Date())    // 设置签发时间(当前时间)
                .expiration(new Date(System.currentTimeMillis() + expire)) // 设置过期时间
                .signWith(getSecretKey())// 使用密钥签名(保证Token不被篡改)
                .compact();              // 生成最终的 Token 字符串
    }

    /**
     * 解析 Token,获取载荷(Claims)
     * 会自动校验:签名是否正确 + 是否过期
     * @param token 前端传入的 Token
     * @return Token 中的载荷数据
     */
    public Claims extractClaims(String token) {
        return Jwts.parser()
                .verifyWith(getSecretKey()) // 设置密钥,用于校验签名
                .build()                   // 构建解析器
                .parseSignedClaims(token)  // 解析并校验 Token
                .getPayload();             // 获取 Token 中的有效载荷
    }

    /**
     * 从 Token 中提取用户名
     */
    public String extractUsername(String token) {
        return extractClaims(token).getSubject();
    }

    /**
     * 判断 Token 是否过期
     * true = 已过期
     * false = 未过期
     */
    public boolean isExpired(String token) {
        return extractClaims(token).getExpiration().before(new Date());
    }
}

四、登录接口(返回 Token)

@PostMapping("/api/users/login")
public Result login(@RequestBody User user) {
    // 1. 校验用户名密码(你自己写)
    User loginUser = userService.getUserByUsername(user.getUsername());
    if (loginUser == null || !loginUser.getPassword().equals(user.getPassword())) {
        // 你现在收到的错误就是这里!
        return Result.fail(401, "用户名或密码错误");
    }

    // 2. 生成 JWT token
    Map<String, Object> claims = new HashMap<>();
    claims.put("userId", loginUser.getId());
    String token = jwtUtil.generateToken(claims, user.getUsername());

    // 3. 返回 token
    return Result.success(token);
}

五、前端 axios 携带 Token(你需要的)

axios.post('http://localhost:8080/api/users/login', {
  username: 'admin',
  password: '123456'
}).then(res => {
  const token = res.data;
  localStorage.setItem('token', token);
})

// 以后请求携带 token
axios.get('http://localhost:8080/api/user/info', {
  headers: {
    'Authorization': 'Bearer ' + localStorage.getItem('token')
  }
})