建立用於簽署 JWT 令牌的 Spring 安全性金鑰
1. 概述
JSON Web Tokens (JWT) 是保護無狀態應用程式的事實上的標準。 Spring Security 框架提供了將 JWT 整合到安全 REST API 的方法。產生令牌的關鍵過程之一是應用簽名來保證真實性。
在本教程中,我們將探索利用 JWT 身份驗證的無狀態 Spring Boot 應用程式。我們將設定必要的元件並建立一個加密的SecretKey
實例來簽署和驗證 JWT。
2. 項目設定
首先,讓我們使用 Spring Security 和 JWT 令牌引導一個無狀態 Spring Boot 應用程式。
2.1. Maven 依賴項
首先,我們將[spring-boot-starter-web](https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web)
、 spring-boot-starter-security
、 spring-boot-starter-data-jpa
和h2
資料庫相依性新增至pom.xml
:
<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 提供 API 來建立 REST API。此外,Spring Boot Starter Security 依賴項有助於提供身份驗證和授權。我們添加了一個記憶體資料庫以進行快速原型設計。
接下來,讓我們將jjwt-api
、 [jjwt-impl](https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-impl)
和jjwt-jackson
依賴項加入pom.xml
中:
<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>
這些依賴項提供了一個 API 來產生和簽署 JWT 並將其整合到 Spring Security 中。
2.2.智威湯遜配置
首先,讓我們建立一個身分驗證入口點:
@Component
class AuthEntryPointJwt implements AuthenticationEntryPoint {
// ...
}
在這裡,我們建立一個類別來使用 JWT 身份驗證來處理 Spring Security 應用程式中的授權存取嘗試。它充當看門人,確保只有具有有效存取權限的使用者才能存取受保護的資源。
然後,我們建立一個名為AuthTokenFilter
的類,該類攔截傳入請求、驗證 JWT 令牌,並在存在有效令牌時對使用者進行身份驗證:
class AuthTokenFilter extends OncePerRequestFilter {
// ...
}
最後,讓我們建立JwtUtil
類,它提供建立和驗證令牌的方法:
@Component
class JwtUtils {
// ...
}
此類別包含使用signWith()
方法的邏輯。
2.3.安全性設定
最後,讓我們定義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 入口點和過濾器來啟動 JWT 身份驗證。
signWith()
方法
JJWT 函式庫提供了signWith()
方法來幫助使用特定的加密演算法和金鑰對 JWT 進行簽署。此簽名過程對於確保 JWT 的完整性和真實性至關重要。
signWith()
方法接受Key
或SecretKey
實例和簽章演算法作為參數。基於哈希的訊息驗證程式碼 (HMAC) 演算法是最常用的簽章演算法之一。
重要的是,該方法需要一個金鑰(通常是位元組數組)來進行簽章過程。我們可以使用Key
或SecretKey
實例將秘密字串轉換為秘密密鑰。
值得注意的是,我們可以傳遞一個普通字串作為金鑰。然而,這缺乏加密Key
或SecretKey
實例的安全保證和隨機性。
使用SecretKey
實例可確保 JWT 的完整性和真實性。
4. 簽署智威湯遜
我們可以使用Key
和SecretKey
實例建立一個強密鑰來簽署 JWT。
4.1.使用Key
實例
本質上,我們可以將秘密字串轉換為Key
實例,以在使用它簽署 JWT 之前對其進行進一步加密。
首先,我們確保秘密字串是 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 演算法的金鑰。
如果金鑰不是 Base64 編碼的,我們可以對純字串呼叫getByte()
方法:
private Key getSigningKey() {
byte[] keyBytes = this.jwtSecret.getBytes(StandardCharsets.UTF_8);
return Keys.hmacShaKeyFor(keyBytes);
}
但是,不建議這樣做,因為密鑰的格式可能很差,並且字串可能包含非 UTF-8 字元。因此,我們必須確保密鑰字串是 Base64 編碼的,然後才能從中產生密鑰。
4.2.使用SecretKey
實例
此外,我們可以使用 HMAC-SHA 演算法形成強金鑰來建立SecretKey
實例。讓我們建立一個傳回金鑰的SecretKey
實例:
SecretKey getSigningKey() {
return Jwts.SIG.HS256.key().build();
}
這裡,我們直接使用HMAC-SHA演算法,不使用位元組數組。這形成了強大的簽名密鑰。接下來,我們可以透過傳遞getSigningKey()
作為參數來更新signWith()
方法。
或者,我們可以從 Base16 編碼字串建立SecretKey
實例:
SecretKey getSigningKey() {
byte[] keyBytes = Decoders.BASE64.decode(jwtSecret);
return Keys.hmacShaKeyFor(keyBytes);
}
這會產生一個強SecretKey
類型來簽署和驗證 JWT。
值得注意的是,建議使用SecretKey
實例而不是Key
實例,因為用於驗證令牌的名為verifyWith()
的新方法接受SecretKey
類型作為參數。
4.3.應用程式密鑰
現在,讓我們應用密鑰來簽署我們應用程式的 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. 結論
在本文中,我們學習如何使用 Java Key 和 SecretKey 實例建立金鑰。此外,我們還看到了一個無狀態 Spring Boot 應用程序,它利用 JWT 令牌來保證令牌完整性,並應用Key
或SecretKey
實例來對其進行簽署和驗證。不再建議使用純字串。
與往常一樣,範例程式碼的完整原始程式碼可在 GitHub 上取得。