POST / POSTS

在 Java 和 SpringBoot 中使用 JWT

AI摘要
本文详细介绍了如何在纯 Java 环境和 Spring Boot 项目中集成和使用 JSON Web Token (JWT)。内容涵盖了添加依赖、生成 Token、验证并解析 Token 的基本操作,并重点讲解了如何在 Spring Boot 中通过编写 JWT 工具类和配置拦截器,实现对 API 接口的统一认证和授权管理,同时列举了常见的验证异常及其处理方式。

JSON Web Token (JWT) 是一种开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于在各方之间安全地传输信息。由于其体积小、易于传输的特性,常被用于身份认证和授权。本文将介绍如何在纯 Java 环境以及 Spring Boot 项目中集成和使用 JWT。

一. 在 Java 中使用 JWT

在标准的 Java 环境中使用 JWT,通常分为三个步骤。

1. 导入依赖

首先,在您的 Maven 项目的 pom.xml 文件中引入 java-jwt 库。

XML
 <dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.18.1</version>
 </dependency>

2. 生成 Token

生成 Token 时,可以定义 payload(载荷)中的自定义信息、设置过期时间,并使用一个密钥(secret)进行签名。

JAVA
// 1. 设置过期时间,例如:120秒后
Calendar time = Calendar.getInstance();
time.add(Calendar.SECOND, 120);

// 2. 创建 JWT 并签名
String token = JWT.create()
        .withClaim("userId", 12) // 设置 payload 部分的自定义声明
        .withClaim("username", "Heaven")
        .withExpiresAt(time.getTime())  // 设置过期时间
        .sign(Algorithm.HMAC256("!#^DHAD%&(")); // 使用 HMAC256 算法和密钥进行签名

System.out.println(token);

生成的 Token 是一串由三部分(Header, Payload, Signature)组成的字符串,用 . 分隔。

3. 解析并验证 Token

接收到 Token 后,需要使用相同的密钥和算法来验证其合法性,并解析出其中的数据。如果验证失败(如签名不匹配、Token 过期),将会抛出异常。

JAVA
// 1. 使用相同的算法和密钥创建验证器
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("!#^DHAD%&(")).build();

// 2. 验证 token
DecodedJWT decodedJWT = jwtVerifier.verify(token);

// 3. 从解析后的 token 中获取数据
System.out.println(decodedJWT.getClaim("userId").asLong());
System.out.println(decodedJWT.getClaim("username").asString());
System.out.println(decodedJWT.getExpiresAt());

二. 在 Spring Boot 中集成 JWT

在 Spring Boot 项目中,通常将 JWT 验证逻辑与拦截器(Interceptor)结合,以实现对 API 接口的统一认证和授权管理。

提示:常见的 JWT 验证异常

  • SignatureVerificationException: 签名不一致或无效。
  • TokenExpiredException: Token 已过期。
  • AlgorithmMismatchException: 使用的算法与 Token 中的算法不匹配。
  • InvalidClaimException: payload 中的声明无效。

1. 编写 JWT 工具类

首先,创建一个工具类来封装 Token 的生成和验证逻辑,便于在项目中复用。

JAVA
package com.heaven.report.utils;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import java.util.Calendar;
import java.util.Map;

public class JWTUtils {
    // 定义一个固定的密钥,实际项目中建议从配置文件读取
    private static final String SECRET = "FFz%A#ZYj8C5AtHfkaDX8qPK7QUDeKce16rHWMKREMORpa8eue&2sdwFXa0oFVhN";

    /**
     * 生成 Token
     * @param payloadMap 包含在 payload 中的数据
     * @return 生成的 Token 字符串
     */
    public static String createToken(Map<String, String> payloadMap) {
        Calendar instance = Calendar.getInstance();
        // 默认设置为3天后过期
        instance.add(Calendar.DATE, 3);

        JWTCreator.Builder builder = JWT.create();
        // 将 payload 数据添加到 token 中
        payloadMap.forEach(builder::withClaim);

        String token = builder.withExpiresAt(instance.getTime()) // 设置过期时间
                .sign(Algorithm.HMAC256(SECRET)); // 设置签名算法和密钥
        return token;
    }

    /**
     * 验证 Token 并返回解码后的信息
     * @param token 待验证的 Token
     * @return DecodedJWT 对象,包含了 token 中的信息
     */
    public static DecodedJWT verify(String token) {
        // 如果验证失败,此方法会抛出异常
        return JWT.require(Algorithm.HMAC256(SECRET)).build().verify(token);
    }
}

2. 创建拦截器

创建一个 Spring MVC 拦截器,在请求到达 Controller 之前,从请求头中获取 Token 并进行验证。

JAVA
public class JWTInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        Map<String, Object> map = new HashMap<>();
        // 从请求头中获取 token
        String token = request.getHeader("token");

        try {
            // 使用工具类验证 token
            JWTUtils.verify(token);
            // 验证通过,放行请求
            return true;
        } catch (SignatureVerificationException e) {
            e.printStackTrace();
            map.put("msg", "无效的签名");
        } catch (TokenExpiredException e) {
            e.printStackTrace();
            map.put("msg", "Token 已过期");
        } catch (AlgorithmMismatchException e) {
            e.printStackTrace();
            map.put("msg", "Token 算法不一致");
        } catch (Exception e) {
            e.printStackTrace();
            map.put("msg", "Token 无效");
        }
        
        // 设置响应状态为 false
        map.put("state", false);

        // 将 map 转换为 JSON 字符串并返回给前端
        String json = new ObjectMapper().writeValueAsString(map);
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().println(json);
        
        // 拦截请求
        return false;
    }
}

3. 注册并配置拦截器

最后,创建一个配置类实现 WebMvcConfigurer 接口,将拦截器注册到 Spring Boot 应用中,并指定需要拦截和排除的路径。

JAVA
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new JWTInterceptor())
                .addPathPatterns("/**") // 拦截所有路径
                .excludePathPatterns("/api/user/login"); // 排除登录等不需要验证的接口
    }
}

通过以上配置,除了 /api/user/login 接口外,所有其他接口的请求都会被 JWTInterceptor 拦截和验证。