使用 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