mcpField
S
spring-security-jwt

spring-security-jwt

github.com/rrezartprebrezaセキュリティ328125 回インストール6 日前に更新
リポジトリ

spring-security-jwt について

Spring 開発者向けの JWT セキュリティスキルです。ステートレスな Bearer 認証、アカウント状態の確認、トークン処理のテンプレートと指針を提供し、よくある設定ミスを防ぎます。

主な機能

  1. 01JWT サービスと認証フィルターのテンプレート
  2. 02ステートレスな Bearer 認証設定
  3. 03ロールおよびメソッド単位の認可
  4. 04JSON による 401・403 エラー処理
  5. 05アクセストークンとリフレッシュトークンの指針

活用シーン

  1. 01Spring API への JWT Bearer 認証の追加
  2. 02トークン認証時のアカウント状態確認
  3. 03エンドポイントとメソッドへのロールベースアクセス制御

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/rrezartprebreza/spring-boot-skills --skill spring-security-jwt

インストール後、Claude Code で/spring-security-jwtを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

agents/
openai.yaml
examples/
bad-security-config.java
good-security-config.java
templates/
JwtAuthenticationFilter.java
JwtService.java
SecurityConfig.java

詳細

Spring Security — JWT

依存関係

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.12.6</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.12.6</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.12.6</version>
    <scope>runtime</scope>
</dependency>

セキュリティ設定

コンパイル済みのSecurityConfigを、以下のフィルターおよびサービスとともに使用します。 この設定では、ラムダ DSL、ステートレスなベアラー認証、ロールルール、JSON 形式の 401/403 ハンドラーを使用し、セキュリティチェーンのフィルターがサーブレットに登録されないようにしています。Bean メソッドインジェクションを使うことで、設定クラスと、その設定クラス自身の AuthenticationProvider Bean の間で発生するコンストラクターの循環依存を回避します。プロジェクトに合わせてルートとロールを調整してください。CSRF の無効化は、ヘッダーのみのベアラー API に適用されます。ブラウザーが認証 Cookie を自動的に送信する場合は、CSRF 保護を維持してください。

JWT の実装

テスト済みのJwtServiceと JwtAuthenticationFilterのテンプレートを組み合わせて使用します。 設定キーはapp.jwt.secret、app.jwt.access-token-expiration、および app.jwt.refresh-token-expirationです(期間の単位はミリ秒です)。

このフィルターは、有効期限切れ、形式不正、改ざん済み、有効期限の欠落したトークン、およびリフレッシュトークンを拒否し、401 と Bearer チャレンジを返します。認証の前に、現在のユーザーの有効、ロック、アカウント期限切れ、および認証情報期限切れの各フラグを確認します。削除済みユーザーにも 401 を返します。データベースの停止や下流のアプリケーション障害は、無効な認証情報として処理せず、サーバーエラーのままにする必要があります。無効なトークンが提示された場合は、公開エンドポイントでも拒否します。

フィルターのエラー応答例では Problem Details を使用します。既存のレガシー API では、確立済みのエラー契約に合わせて、この応答と以下のエントリーポイントを調整してください。ベアラートークンをログに記録しないでください。フィルター Bean はセキュリティチェーン内でのみ登録します。enabledフラグを false に設定したFilterRegistrationBean<JwtAuthenticationFilter>を使って、サーブレットコンテナーへの登録を無効にしてください。

これらのテンプレートは、単一サービス内で利用するファーストパーティのトークン契約を例示しています。サービス間で署名キーを共有したり、サービス間でトークンを受け入れたりする前に、発行者とオーディエンス、キーのローテーション、および失効を定義して検証してください。Spring Security のリソースサーバーサポートでもカスタム JWT を検証できます。アプリケーションにすでに適合している場合は、そのサポートを維持してください。トークンの生成だけでは、完全なリフレッシュフローにはなりません。以下のローテーションと再利用検出の要件を維持してください。

JSON 形式の 401/403

設定とフィルターのテンプレートでは、認証および認可エラーに Problem Details を使用します。レガシーのエラー契約を使う場合は、両方を合わせて調整してください。保護されたルートで認証情報がない場合は 401 を返します。認証済みの呼び出し元に必要なロールがない場合は 403 を返します。無効なトークンが提示された場合は、そのルートで匿名アクセスが許可されていても 401 を返します。 コントローラーアドバイスでは、ディスパッチャーサーブレットより前にスローされた例外を処理できません。

認証コントローラー

@RestController
@RequestMapping("/api/v1/auth")
@RequiredArgsConstructor
public class AuthController {

    private final AuthService authService;

    @PostMapping("/login")
    public ApiResponse<AuthResponse> login(@Valid @RequestBody LoginRequest request) {
        return ApiResponse.ok(authService.login(request));
    }

    @PostMapping("/refresh")
    public ApiResponse<AuthResponse> refresh(@Valid @RequestBody RefreshRequest request) {
        return ApiResponse.ok(authService.refresh(request.refreshToken()));
    }

    @PostMapping("/register")
    public ResponseEntity<ApiResponse<AuthResponse>> register(@Valid @RequestBody RegisterRequest request) {
        return ResponseEntity.status(201).body(ApiResponse.ok(authService.register(request)));
    }
}

public record AuthResponse(String accessToken, String refreshToken, long expiresIn) {}

メソッドレベルのセキュリティ

// On service methods
@PreAuthorize("hasRole('ADMIN')")
public void deleteUser(UUID userId) { ... }

@PreAuthorize("hasRole('ADMIN') or #userId == authentication.principal.id")
public UserProfile getProfile(UUID userId) { ... }

@PostAuthorize("returnObject.email == authentication.name")
public User findById(UUID id) { ... }

application.yml

app:
  jwt:
    secret: ${JWT_SECRET} # min 256-bit base64 encoded key
    access-token-expiration: 900000   # 15 minutes
    refresh-token-expiration: 604800000 # 7 days

この例ではリフレッシュトークンを作成しますが、リフレッシュエンドポイントは実装していません。本番環境のリフレッシュフローでは、type=refreshのトークンのみを受け入れ、使用のたびにリフレッシュトークンをローテーションし、以前のトークンを失効させる必要があります(たとえば、データベースまたは Redis にトークンファミリーのハッシュ化されたレコードを保存します)。

注意点

  • エージェントがユーザー検索時に AuthenticationException を広く捕捉します。サーバー障害として AuthenticationServiceException を維持してください。
  • エージェントが有効な JWT を使って無効化またはロックされたユーザーをログインさせます。クレームだけでなく、現在のアカウント状態も検証してください。
  • エージェントが古い API の HttpSecurity.csrf().disable() を使います。AbstractHttpConfigurer::disable を使ってください。
  • エージェントが ExpiredJwtException をフィルターから漏らします。期限切れトークンは 401 ではなく 500 になるため、フィルター内で捕捉してください。
  • エージェントが exceptionHandling() を省略します。クライアントには空の 401/403 レスポンス本文(またはログインページへのリダイレクト)が返されます。@RestControllerAdvice ではフィルターレベルの例外を捕捉できません。
  • エージェントが JWT シークレットをコード内に保存します。常に環境変数の ${JWT_SECRET} を使ってください(HS256 には 256 ビット以上のキーが必要です。そうでない場合、Keys.hmacShaKeyFor が WeakKeyException をスローします)。
  • エージェントが SessionCreationPolicy.IF_REQUIRED を使います。JWT では STATELESS にする必要があります。
  • エージェントが署名と有効期限しか検証しません。ベアラーフィルターでは type=access トークンのみを受け付ける必要があります。リフレッシュトークンは専用のリフレッシュエンドポイントで扱ってください。
  • エージェントが @PreAuthorize の動作に必要な @EnableMethodSecurity を設定し忘れます。
  • エージェントが BCrypt の強度を 10 未満に設定します。本番環境では 12 を使ってください。
  • エージェントがトークン検証ロジックをコントローラーに配置します。これはフィルターに配置してください。
  • エージェントが例でリフレッシュトークンを localStorage に保存します。httpOnly クッキーまたは安全なストレージを推奨してください。リフレッシュトークンは有効期間の長い認証情報です。

コメント