🔐 Spring Framework 講座【第37回】スケーラブルな認証方式!〜JWT(JSON Web Token)の導入〜

docs

前回、モダンなフロントエンド(SPA)との連携において、CORS(クロスオリジンリソース共有)の設定が不可欠であることを学びました。

SPAとの連携でしばしば採用されるのが、JWT(JSON Web Token)という認証方式です。これまでに学んだセッションベースの認証と異なり、JWTはステートレス(状態を持たない)な認証を実現し、システムのスケーラビリティを大きく向上させます。

今回は、JWTとは何か、なぜSPAで好まれるのか、そしてSpring SecurityにJWT認証を組み込むための基本を学びます。


1. セッション認証 vs. JWT認証

Spring Securityのデフォルトであるセッションベース認証は、サーバーが認証情報をセッションという形で保持します。

認証方式認証情報の保持場所特徴
セッション認証サーバー側のメモリやDBサーバーが状態を管理(ステートフル)。シングルサーバーでは簡単だが、サーバー増設時にセッション共有が必要。
JWT認証クライアント側(ブラウザのローカルストレージなど)サーバーが状態を管理しない(ステートレス)。認証情報をトークンに含め、リクエストごとに検証する。

JWT認証は、サーバーが増減してもセッション共有の仕組みが不要なため、マイクロサービス大規模な分散システムにおいて非常に有利です。


2. JWT(JSON Web Token)の構造

JWTは、認証情報やユーザー情報(クレームと呼びます)を格納した、署名付きの文字列です。以下の3つのパートがピリオド(.)で区切られて構成されます。

2-1. JWTの3つのパート

  1. Header(ヘッダー): トークンのタイプ(JWT)と、署名に使われたアルゴリズム(例:HMAC SHA256)を定義します。
  2. Payload(ペイロード): クレーム(Claims)と呼ばれる実際のユーザー情報を格納します。ユーザーID、権限(Role)、トークンの発行時刻や有効期限などが含まれます。
  3. Signature(署名): ヘッダーとペイロードを、サーバーが持つ秘密鍵で暗号化したハッシュ値です。

2-2. 署名の役割(改ざん検知)

署名が存在することで、サーバーは以下のことを確認できます。

  1. 正真性: トークンが本物のサーバーによって発行されたものかを確認できる。
  2. 改ざん検知: トークンの内容(ペイロード)が、発行後に第三者によって改ざんされていないかを確認できる。

注意: ペイロードは誰でもデコードして中身を見ることができます。機密情報(パスワードなど)を格納してはいけません


3. Spring SecurityへのJWT組み込み

Spring SecurityでJWT認証を有効にするには、主に以下の手順が必要です。

3-1. セキュリティ設定の変更(ステートレス化)

まず、セッションを使わないステートレスな認証を行うよう、Spring Securityに設定します。

Java

import org.springframework.security.config.http.SessionCreationPolicy;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            // セッションを使わないよう設定(最も重要)
            .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) 
            
            // 認証されていないリクエストは全て弾く
            .authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
            
            // CSRF対策もステートレス認証では基本的に無効化(トークンで対策するため)
            .csrf(csrf -> csrf.disable()); 

        // ... JWT検証のためのカスタムフィルターを追加する(次項)

        return http.build();
    }
    // ... PasswordEncoder, UserDetailsServiceなどのBean定義は継続
}

3-2. 認証後のJWT発行

ユーザーがログインに成功した後(UserDetailsService で認証が成功した後)、サーバーは秘密鍵を使ってJWTを生成し、それをクライアント(ブラウザ)に返します。

3-3. リクエストごとのJWT検証(カスタムフィルター)

クライアントは、発行されたJWTをHTTPヘッダー(通常は Authorization: Bearer <JWT>の形式)に入れて、APIリクエストを送信します。

Spring Securityでは、このリクエストヘッダーからJWTを取り出し、署名を検証し、ペイロードの有効期限を確認し、認証情報を SecurityContext にセットするためのカスタムフィルターを実装する必要があります。

Java

// JWTFilter.java (概念)
public class JwtAuthenticationFilter extends OncePerRequestFilter {
    
    @Override
    protected void doFilterInternal(...) {
        // 1. リクエストヘッダーからJWTを取得
        // 2. JWTの署名と有効期限を検証
        // 3. 認証がOKであれば、UserDetailsオブジェクトを作成
        // 4. SecurityContextHolder.getContext().setAuthentication(Authentication) で認証情報をセット
    }
}

このカスタムフィルターを SecurityFilterChain に追加することで、以降のControllerやServiceの処理で、認証されたユーザー情報(権限など)を利用できるようになります。

✅ 本日のまとめ

  • **JWT(JSON Web Token)**は、ステートレスな認証を実現し、スケーラビリティに優れるため、SPAやマイクロサービスで広く利用される。
  • JWTは、ヘッダー、ペイロード、署名の3パートからなり、署名によって改ざん検知が可能である。
  • JWTのペイロードは誰でも見られるため、機密情報を格納してはならない
  • Spring SecurityでJWTを導入するには、セッション管理を SessionCreationPolicy.STATELESS に設定し、リクエストヘッダーのJWTを処理するためのカスタムフィルターを実装する必要がある。

🔔 次回予告

ここまで、Webアプリケーション構築に必要な全ての要素を学びました。

次回からは、アプリケーション開発の次のステップである「テスト」というテーマに進みます。次回は、Spring Bootにおける**単体テスト(JUnit 5)**の書き方と、DIコンテナを意識しないテストの基本を学びます。

次回:【第38回】堅牢なシステムのために!〜単体テスト(Unit Test)の基本〜 にご期待ください!

コメント

タイトルとURLをコピーしました