Spring Securityのコアコンセプトの詳細解説

1. Spring Securityが解決する課題とは?(「なぜ存在するのか」から始めよう)

どのWebシステムでも直面する共通の課題があります。

  • ユーザーはどのようにログインするか?
  • ログイン状態はどのように保持するか?
  • li>各リクエストがログイン済みかどうかをどのように判断するか?
  • 異なるユーザーが異なるインターフェースにアクセスする場合、どのように権限を制御するか?
  • ログイン失敗や無権限の場合、どのように統一されたレスポンスを返すか?

これらをすべて自前で実装しようとすると、以下のようなコードが大量に生まれます。

  • インターセプター
  • if-else文の羅列
  • セッションの判定ロジック
  • 繰り返し発生する権限チェックコード

Spring Securityの目標:これらの「セキュリティ問題」をビジネスロジックから完全に分離する

1.1. Spring Securityの公式な位置づけ

Spring Securityは、Springエコシステムにおけるセキュリティフレームワークであり、アプリケーションに対して以下を提供します。

  • 認証(Authentication):ユーザーが誰であるかを確認するプロセス
  • 認可(Authorization):認証されたユーザーが何をできるかを判断するプロセス
  • リクエストレベルのセキュリティ保護:フィルターチェーンに基づく実装

1.2. Spring Securityを一言で理解する

Spring Security =
「フィルターチェーン」に基づいたセキュリティフレームワークであり、
リクエストがビジネスロジックに入る前に、
「誰であるか」と「何ができるか」を判断する役割を担います。

2. 全体のアーキテクチャ思想

2.1. 核となる設計思想

Spring Securityの設計は、4つの核心原則に従っています。

  1. ビジネスコードとの完全な分離
  2. Spring MVCとの分離
  3. すべてのセキュリティ制御をリクエストの入り口に集中させる
  4. 高い拡張性(特定の認証方式に縛られない)

これにより、以下の結論が導かれます。
👉 Spring Securityは、ControllerやInterceptorではなく、Filterである必要があります。

2.2. なぜFilterでなければならないのか?(Interceptorではない理由)

比較項目 Filter Interceptor
レベル Servletコンテナレベル Spring MVCレベル
カバー範囲 すべてのリクエスト Controllerのみをインターセプト
実行順序 最も早い Filterより遅い
セキュリティ より高い 迂回のリスクあり

セキュリティは、できるだけ早い段階で処理されるべきです。これがFilterの重要性です。

3. Spring Securityの「骨格コンポーネント」

以下は、一連の実行フローの概要です。

コンポーネント 日本語名 本質的な役割
SecurityFilterChain セキュリティフィルターチェーン すべてのセキュリティロジックの入り口
Authentication 認証情報 「誰であるか」の結果
AuthenticationManager 認証マネージャ 認証の総合エントリーポイント
AuthenticationProvider 認証プロバイダ 実際の認証処理を実行
UserDetailsService ユーザー情報サービス システムからユーザーを検索
UserDetails ユーザー詳細モデル Securityが理解できるユーザーモデル
GrantedAuthority 権限情報 権限ポイント
SecurityContext セキュリティコンテキスト ログイン状態を保持するコンテナ

ここから、実際の実行順序に沿って説明します。

4. 認証(Authentication)——誰であるか?

4.1. 認証とは(ログインページではない)

認証 = 何らかの証明情報を使って、自分が誰であるかを証明すること

  • ユーザー名 + パスワード
  • Token / JWT
  • サードパーティログイン情報

ログインは、認証の一つのUI的な表現に過ぎません。

4.2. なぜAuthenticationオブジェクトを单独で設計する必要があるのか?

Spring Securityは、統一された認証結果モデルを必要としています。どの方式(ユーザー名パスワード、JWT、OAuth2など)を使用しても、最終的に同じ構造に変換する必要があるからです。

これがAuthentication(認証情報)です。

4.3. Authentication(認証情報)の内部構造の詳細

`Authentication`は本質的にインターフェースであり、核心となるフィールドは以下の通りです。

フィールド 意味
principal ユーザー主体(通常はUserDetails)
credentials 証明情報(例:パスワード。認証後はクリアされる)
authorities 権限リスト
authenticated 認証が成功したかどうか

**ログイン済みかどうかの判断は、セッションではなく、`authenticated`フラグの真偽値で行われます。**

4.4. 認証フローの概要

  1. リクエストが到着
  2. Filterがリクエストをインターセプト
  3. 「未認証のAuthentication(認証情報)」を構築
  4. AuthenticationManager(認証マネージャ)に渡す
  5. 適切なAuthenticationProvider(認証プロバイダ)を見つける
  6. 検証が成功
  7. 「認証済みのAuthentication(認証情報)」を返却
  8. SecurityContext(セキュリティコンテキスト)に保存

5. AuthenticationManager & AuthenticationProvider

5.1. なぜAuthenticationManager(認証マネージャ)が必要なのか?

それは実務を行うのではなく、「ディスパッチセンター」の役割を担います。

理由は一つだけです。
👉 Spring Securityは、いかなる認証方式にも縛られないためです。

  • ユーザー名パスワード?
  • JWT?
  • LDAP?
  • OAuth2?

その役割は、「認証リクエストを"適切な人"に渡すこと」だけです。

5.2. AuthenticationProvider(認証プロバイダ):実際の作業を行う人

一つのProvider = 一つの認証方式

例えば:

Provider 役割
DaoAuthenticationProvider ユーザー名パスワード認証
JwtAuthenticationProvider JWT認証
カスタムProvider ビジネスロジックによる認証

6. UserDetailsService(ユーザー情報サービス) & UserDetails(ユーザー詳細モデル)

6.1. なぜSpring Securityは直接データベースを検索しないのか?

理由は以下の通りです。

  • ユーザーテーブルの構造が分からない
  • 権限モデルの設計方法が分からない

そこで、中間プロトコルを定義しています。

6.2. UserDetailsService(ユーザー情報サービス)

実装する必要があるインターフェースです。

UserDetails loadUserByAccount(String account);

役割は一つだけです。

「あなたのシステム内のユーザー」を、「Spring Securityが理解できるユーザー」に変換する

6.3. UserDetails(ユーザー詳細モデル)

これはSpring Securityがユーザーを統一して理解するための方法であり、以下を含みます。

  • ユーザー名
  • パスワード
  • 無効/ロック状態
  • 権限リスト(GrantedAuthority)

あなたのユーザーテーブル ≠ UserDetails

7. 認可(Authorization)——何ができるか?

7.1. 認可の本質

「誰であるか」が確定した後、「このリソースにアクセスする資格があるか」を判断すること

7.2. GrantedAuthority(権限情報)

これはSpring Securityが唯一認識する権限モデルです。

  • 一つの文字列
  • 一つの権限ポイントを表す

例:

ROLE_ADMIN
user:add
order:query

ロールは、単なる権限の一種の約束事に過ぎません。

7.3. 認可はどこで発生するのか?

FilterSecurityInterceptor(セキュリティインターセプタ)

このコンポーネントは以下の処理を行います。

  1. 現在のリクエストURLを取得
  2. アクセスルールを読み込む
  3. SecurityContext(セキュリティコンテキスト)からユーザーの権限を取得
  4. 許可/拒否(403)を決定

8. SecurityContext(セキュリティコンテキスト)

8.1. SecurityContextとは?

現在のリクエスト/スレッドのセキュリティコンテキスト

中には一つのものしか保存しません。

Authentication(認証情報)

8.2. なぜセッションを直接チェックしないのか?

理由は以下の通りです。

  • JWTはステートレスである
  • マイクロサービス環境ではセッションが必ずしも存在しない
  • SecurityContextはより汎用的

セッションは、SecurityContextの一つの保存方法に過ぎません。

8.3. 現在のユーザーを正しく取得する方法

SecurityContextHolder
    .getContext()
    .getAuthentication();

すべての認可、監査、権限判断は、この情報に基づいて行われます。

9. Filter Chain(フィルターチェーン)

9.1. 本質的な理解

Spring Security = 責任連鎖パターンに基づいたFilterのチェーン

9.2. 主要なFilter

Filter 日本語説明
UsernamePasswordAuthenticationFilter フォームログイン認証
OncePerRequestFilter リクエストごとに一度(JWTでよく使われる)
ExceptionTranslationFilter 例外の翻訳
FilterSecurityInterceptor 最終的な認可

JWT検証Filterは、認可の前に実行される必要があります。

10. JWTとステートレス認証

10.1. なぜJWTが登場したのか?

理由は以下の通りです。

  • セッションはマイクロサービスに不向き
  • 分散環境でのセッションのコストが高い

10.2. JWT + Spring Securityの本質

JWTは「認証情報のキャリア」に過ぎない

本当に重要なのは以下の点です。

  • JWTの検証
  • Authenticationの構築
  • SecurityContextへの書き込み

JWTは、Spring Securityのコアモデルを変えるものではありません。

11. 401 vs 403

ステータスコード 意味
401 未認証(ログインしていない)
403 認証済みだが権限がない

12. ロールベースのアクセス制御(RBAC)

12.1. RBACとは?

RBACの核心思想は以下の通りです。

権限をユーザーに直接割り当てるのではなく、ロールに割り当てる

関係モデルは以下の通りです。

ユーザー(User)
   ↓
ロール(Role)
   ↓
権限(Permission)
   ↓
リソース(URL / メソッド)

なぜ企業システムのほとんどがRBACを使用するのか?

問題 ユーザー-権限直接リンク RBAC
メンテナンスコスト 非常に高い 低い
権限の再利用 不可 可能
拡張性 悪い 良い
企業向け

RBACの本質:権限のグループ化 + 疎結合

12.2. Spring Securityは「ネイティブに」RBACをサポートしていますか?

Spring Security自体はRBACを実装していません。`GrantedAuthority(権限情報)`に基づいた汎用的な認可モデルを提供するだけです。RBACは、「ロール → 権限 → GrantedAuthority(権限情報)」のマッピング方式によって実現されます。

12.3. RBACのSpring Securityにおけるマッピング関係

RBACの概念 Spring Securityの対応
ユーザー UserDetails
ロール ROLE_xxx(GrantedAuthority)
権限 GrantedAuthority
セッションのセキュリティ状態 Authentication

Spring Securityは文字列の権限しか認識せず、「ロールテーブル」を直接認識しません。

12.4. RBACの完全な実装フロー

ステップ1:データベースモデル

user
role
permission
user_role
role_permission

ステップ2:UserDetailsServiceでロールと権限を読み込む

List<String> permissionList =
    rolePermissionMapper.findPermissionsByUserId(userId);

List<GrantedAuthority> authorityList =
    permissionList.stream()
        .map(SimpleGrantedAuthority::new)
        .collect(Collectors.toList());

ロールであれ権限であれ、最終的にGrantedAuthorityに変換する必要があります。

ステップ3:Authenticationに注入する

return new UsernamePasswordAuthenticationToken(
    userDetails,
    null,
    authorityList
);

ステップ4:ロール/権限に基づいたアクセス制御

http.authorizeHttpRequests()
    .requestMatchers("/admin/**").hasRole("ADMIN")
    .requestMatchers("/order/**").hasAuthority("order:query")
    .anyRequest().authenticated();

13. メソッドレベルの権限制御(Method Security)

13.1. なぜURLレベルの権限だけでは不十分なのか?

URLレベルの権限では、以下の問題を解決できません。

  • Serviceレイヤーの呼び出し
  • 同じインターフェースでも、異なるパラメータで異なる権限
  • 内部メソッドの保護

これがメソッドレベルの権限が存在する意味です。

13.2. Spring Securityが提供するメソッドレベルの権限アノテーション

アノテーション 説明
`@PreAuthorize` メソッド実行前の検証(最もよく使われる)
`@PostAuthorize` メソッド実行後の検証
`@Secured` ロールに基づく
`@RolesAllowed` JSR-250標準

13.3. @PreAuthorizeの底層原理

@PreAuthorize("hasAuthority('user:add')")
public void addUser() {}

実行フロー:

  1. メソッドが呼び出される
  2. Spring AOPがインターセプト
  3. SecurityContextからAuthenticationを取得
  4. Spring式を実行
  5. 権限を判断
  6. 許可するか決定

本質:AOP + SecurityContext + Spring式

14. 動的権限制御

14.1. 動的権限とは?

権限ルールをコードにハードコードせず、データベースから取得する

例:

/admin/**  → ROLE_ADMIN
/user/**   → user:query
/order/**  → order:query

14.2. なぜ企業は動的権限を必ず実装するのか?

  • 権限は頻繁に変更される
  • コードを毎回変更したくない
  • バックエンドで設定可能
  • グレードリリース

14.3. Spring Securityで動的権限を実装する方法

核心思想は一言で言えば:

リクエストが来たときに、「現在のリクエストに何の権限が必要か」を動的に計算する

実装方法:

  • カスタム`AuthorizationManager`
  • またはカスタム`AccessDecisionManager`
  • URL → 権限のマッピングはデータベースから取得

Spring Securityは「判断」を担当し、「ルールのソース」は担当しません。

15. パスワードセキュリティ機構(PasswordEncoder)

15.1. なぜ平文でパスワードを保存してはいけないのか?

  • データベース漏洩のリスク
  • 内部者のリスク
  • コンプライアンスの問題

15.2. PasswordEncoderの役割

「暗号化 + パスワード検証」を担当

よく使われる実装:

実装 説明
BCryptPasswordEncoder 推奨(デフォルト)
NoOpPasswordEncoder 平文(テスト用のみ)

15.3. BCryptの核心的な利点

  • ランダムなソルトを内蔵
  • 同じパスワードでも、複数回暗号化すると結果が異なる
  • レインボーテーブル攻撃を防ぐ

レインボーテーブル攻撃 = 攻撃者が事前に「一般的な平文パスワード → ハッシュ値」を計算してテーブルに保存しておき、データベースからハッシュ値を入手した際に、テーブルを直接参照して元のパスワードを逆算する攻撃。

16. CSRF、CORS

16.1. CSRF(クロスサイトリクエストフォージェリ)

CSRFの本質

ブラウザがCookieを自動的に送信する特性を利用した攻撃

Spring Securityの対策

  • デフォルトでCSRF保護を有効化
  • Tokenに基づく検証

JWTプロジェクトでは通常、CSRFを無効化します。

16.2. CORS(クロスオリジンリソースシェアリング)

Spring Securityでは:

  • CORSはWebセキュリティの一部
  • 明示的に許可する必要がある
http.cors().and().csrf().disable();

17. まとめ

Spring Securityの本質は以下の通りです。

  1. フィルターチェーンを使用し、リクエストがビジネスロジックに入る前に認証を完了させる。
  2. 認証結果を`Authentication(認証情報)`として統一し、`SecurityContext(セキュリティコンテキスト)`に保存する。
  3. リソースにアクセスする際に、`GrantedAuthority(権限情報)`に基づいて認可を判断する。

タグ: Spring Security Java 認証 認可 JWT

7月7日 22:21 投稿