知識庫 / Spring / Spring Security RSS 訂閱

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

Spring Security
HongKong
5
11:16 AM · Dec 06 ,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 依賴

首先,添加以下 Maven 依賴: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.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 提供 API 用於構建 REST 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>

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

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. <em>signWith()</em> 方法

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

`signWith()` 方法接受 `Key` 或 `SecretKey` 實例以及簽名算法作為參數。 基於消息身份驗證碼 (HMAC) 算法是其中最常用的簽名算法之一。

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

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

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

4. 使用 JWT 進行簽名

我們可以使用 KeySecretKey 實例,創建一個強大的密鑰來對 JWT 進行簽名。

4.1. 使用 Key 實例

本質上,我們可以將秘密字符串轉換為 Key 實例,以便在對它進行進一步加密後使用它來對 JWT 進行簽名。

首先,請確保秘密字符串已進行 Base64 編碼:

private String jwtSecret = "4261656C64756E67";

接下來,讓我們創建一個 對象:

private Key getSigningKey() {
    byte[] keyBytes = Decoders.BASE64.decode(this.jwtSecret);
    return Keys.hmacShaKeyFor(keyBytes);
}

在上述代碼中,我們解碼 jwtSecret 為字節數組。接下來,我們調用 hmacShaKeyFor() 方法,該方法作為參數接受 keyBytesKeys 實例上。這會生成基於 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 算法,而無需使用字節數組。這形成了一個強大的簽名密鑰。接下來,我們可以通過將 signWith() 方法作為參數傳遞,更新 getSigningKey() 方法。

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

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

發佈 評論

Some HTML is okay.