生成 Spring Security 簽名 JWT 令牌的密鑰

Spring Security
Remote
0
11:00 AM · Nov 30 ,2025

1. 概述

JSON Web Tokens (JWT) 是用於安全無狀態應用程序的默認標準。 Spring Security 框架提供將 JWT 集成到安全 REST API 的方法。 生成令牌的關鍵過程之一是應用簽名以確保真實性

在本教程中,我們將探索一個使用 JWT 身份驗證的無狀態 Spring Boot 應用程序。 我們將設置必要的組件並創建一個用於對 JWT 進行簽名和驗證的加密 SecretKey 實例。

2. 項目設置

首先,讓我們使用 Spring Security 和 JWT 令牌對一個無狀態的 Spring Boot 應用程序進行初始化。 值得注意的是,為了簡化和簡潔,我們不會展示完整的設置代碼。

2.1. Maven 依賴

首先,讓我們添加 spring-boot-starter-webspring-boot-starter-securityspring-boot-starter-data-jpa,以及 h2 數據庫 依賴到 pom.xml 中:


<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>3.3.3</version> 
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId> 
    <version>3.3.3</version> 
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
    <version>3.3.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 依賴項提供身份驗證和授權。 我們添加了一個內存數據庫以進行快速原型設計。

接下來,讓我們添加 jjwt-apijjwt-impljjwt-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>

這些依賴項提供生成和對 JWT 進行簽名,並將其集成到 Spring Security 中的 API。

2.2. JWT 配置

首先,讓我們創建一個身份驗證入口點:

@Component
class AuthEntryPointJwt implements AuthenticationEntryPoint {
    // ...
}

在這裏,我們創建一個類來處理 Spring Security 應用程序中 JWT 身份驗證的授權訪問嘗試。 它充當網關,確保只有具有有效訪問權限的用户才能訪問受保護的資源。

然後,讓我們創建一個名為 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 身份驗證。

3. signWith() 方法

JJWT 庫提供 signWith() 方法,用於使用特定的加密算法和密鑰對 JWT 進行簽名。此簽名過程對於確保 JWT 的完整性和真實性至關重要。

signWith() 方法接受 KeySecretKey 實例以及簽名算法作為參數。 哈希消息認證碼 (HMAC) 算法是常用的簽名算法之一

重要的是,該方法需要一個密鑰,通常是一個字節數組,用於簽名過程。我們可以使用 KeySecretKey 實例將密鑰字符串轉換為密鑰。

值得注意的是,我們可以將普通字符串作為密鑰傳遞。但是,這缺乏 KeySecretKey 實例的安全保證和隨機性

使用 SecretKey 實例可確保 JWT 的完整性和真實性。

4. Signing JWT

我們可以創建一個強大的密鑰來使用 KeySecretKey 實例對 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 進行簽名和驗證。

值得注意的是,由於新的 verifyWith() 方法接受 SecretKey 類型作為參數來驗證令牌,因此使用 SecretKey 實例而不是 Key 實例是建議的。

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 令牌進行令牌完整性驗證,並使用 KeySecretKey 實例進行簽名和驗證。 使用純字符串已不再推薦。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.