使用 Key 和 SecretKey 签发 JWT Token
1、概览
JSON Web Tokens(JWT)是用于保护无状态应用的事实上的标准。Spring Security 框架提供了集成 JWT 以保护 REST API 的方法。
本文将带你了解如何在 Spring Boot 应用中创建 SecretKey 实例来签发和验证 JWT。
2、项目设置
2.1、Maven 依赖
首先,在 pom.xml 中添加 spring-boot-starter-web、spring-boot-starter-security、spring-boot-starter-data-jpa 和 h2 数据库依赖:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>3.2.3</version> 
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId> 
    <version>3.2.3</version> 
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
    <version>3.2.3</version>  
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <version>2.2.224</version>
</dependency>
Spring Boot Starter Web 提供了用于构建 REST API 的 API。Spring Boot Starter Security 用于提供身份认证和授权。h2 是一个内存数据库,方便快速开发。
然后,还要在 pom.xml 中添加 jjwt-api、jjwt-impl 和 jjwt-jackson 依赖:
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.12.5</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.12.5</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.12.5</version>
</dependency>
这些依赖提供了生成和签发 JWT 的 API,并将其整合到 Spring Security 中。
2.2、JWT 配置
首先,创建一个身份认证入口点:
@Component
class AuthEntryPointJwt implements AuthenticationEntryPoint {
    // ...
}
这里,创建了一个类来处理 Spring Security 应用中使用 JWT 身份认证的授权访问尝试。它充当门卫,确保只有拥有有效访问权限的用户才能访问受保护的资源。
然后,创建一个 AuthTokenFilter 类,它可以拦截传入的请求,验证 JWT Token,并在 Token 有效情况下对用户进行身份认证:
class AuthTokenFilter extends OncePerRequestFilter {
    // ...
}
最后,创建 JwtUtil 类,它提供了创建和验证 Token 的方法:
@Component
class JwtUtils {
    // ... 
}
该类包含使用 signWith() 方法的逻辑。
2.3、Security Configuration
最后,定义 SecurityConfiguration 类并整合 JWT:
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
class SecurityConfiguration {
    // ...
    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.csrf(AbstractHttpConfigurer::disable)
          .cors(AbstractHttpConfigurer::disable)
          .authorizeHttpRequests(req -> req.requestMatchers(WHITE_LIST_URL)
            .permitAll()
            .anyRequest()
            .authenticated())
          .exceptionHandling(ex -> ex.authenticationEntryPoint(unauthorizedHandler))
          .sessionManagement(session -> session.sessionCreationPolicy(STATELESS))
          .authenticationProvider(authenticationProvider())
          .addFilterBefore(
              authenticationJwtTokenFilter(), 
              UsernamePasswordAuthenticationFilter.class
           );
        return http.build();
    }
    // ...
}
上面的代码中,整合了 JWT 入口和 Filter ,以激活 JWT 身份认证。
3、signWith() 方法
JJWT 提供了一个 signWith() 方法,使用特定加密算法和秘钥签发 JWT。这一签名过程对于确保 JWT 的完整性和真实性至关重要。
signWith() 方法接受 Key 或 SecretKey 实例和签名算法作为参数。基于哈希的消息认证码(HMAC)算法 是最常用的签名算法之一。
该方法需要一个 Secret Key,通常是一个字节数组,用于签名过程。我们可以使用 Key 或 SecretKey 实例将一个 Secret 字符串转换为 Secret Key。
我们可以传递一个普通字符串作为 Secret Key。然而,这种方式缺乏安全性保证和随机性。
使用 SecretKey 实例可确保 JWT 的完整性和真实性。
4、签发 JWT
我们可以使用 Key 和 SecretKey 实例创建一个强大的 Secret Key 来签发 JWT。
4.1、使用 Key 实例
我们可以将一个 Secret String 转换为 Key 实例,在使用它对 JWT 进行签名之前进一步加密。
首先,确保 Secret String 是 Base64 编码的:
private String jwtSecret = "4261656C64756E67";
然后,创建 Key 对象:
private Key getSigningKey() {
    byte[] keyBytes = Decoders.BASE64.decode(this.jwtSecret);
    return Keys.hmacShaKeyFor(keyBytes);
}
在上面的代码中,我们将 jwtSecret 解码为字节数组,然后调用 hmacShaKeyFor(),它接受 keyBytes 作为 Keys 实例的参数。这会根据 HMAC 算法生成 Secret Key。
如果 Secret Key 没有进行 Base64 编码,我们可以在纯字符串上调用 getByte() 方法:
private Key getSigningKey() {
    byte[] keyBytes = this.jwtSecret.getBytes(StandardCharsets.UTF_8);
    return Keys.hmacShaKeyFor(keyBytes);
}
然而,并不建议这样做,因为密钥的格式可能不佳,而且字符串可能包含非 UTF-8 字符。因此,必须确保密钥字符串是 Base64 编码的,然后才能生成 Secret Key。
4.2、使用 SecretKey 实例
此外,还可以使用 HMAC-SHA 算法创建 SecretKey 实例,从而生成一个强大的 Secret Key。
创建一个能返回 Secret Key 的 SecretKey 实例:
SecretKey getSigningKey() {
    return Jwts.SIG.HS256.key().build();
}
在这里,我们直接使用 HMAC-SHA 算法,而不使用字节数组。这就生成了一个强大的签名密钥。接下来,可以通过传递 getSigningKey() 作为参数来更新 signWith() 方法。
或者,也可以通过 BASE64 编码字符串创建 SecretKey 实例:
SecretKey getSigningKey() {
    byte[] keyBytes = Decoders.BASE64.decode(jwtSecret);
    return Keys.hmacShaKeyFor(keyBytes);
}
使用 SecretKey 实例比使用 Key 实例更明智,因为用于验证 Token 的名为 verifyWith() 的新方法接受 SecretKey 作为参数。
4.3、应用 Key
现在,使用 Secret Key 来签发应用的 JWT:
String generateJwtToken(Authentication authentication) {
    UserDetailsImpl userPrincipal = (UserDetailsImpl) authentication.getPrincipal();
    return Jwts.builder()
      .subject((userPrincipal.getUsername()))
      .issuedAt(new Date())
      .expiration(new Date((new Date()).getTime() + jwtExpirationMs))
      .signWith(key)
      .compact();
}
signWith() 方法将 SecretKey 实例作为参数,用于向令牌添加一个唯一的签名。
5、总结
本文介绍了如何在 Spring Boot 应用中使用 Java Key 和 SecretKey 实例创建 Secret Key 来签发 JWT Token,以及这样做的好处是什么。
Ref:https://www.baeldung.com/spring-security-sign-jwt-token
 
				