Spring Securityは、Javaアプリケーションに認証・認可機能を提供する、業界標準のセキュリティフレームワークです。本記事では、Spring Securityの基本概念を理解し、Spring Bootプロジェクトに最小構成でセキュリティを導入する方法を解説します。

実行環境と前提条件

本記事の内容を実践するにあたり、以下の環境を前提としています。

項目 バージョン・要件
Java 17以上
Spring Boot 3.4.x
Spring Security 6.4.x
ビルドツール Maven または Gradle
IDE VS Code または IntelliJ IDEA

事前に以下の準備を完了してください。

  • JDK 17以上のインストール
  • Spring Bootプロジェクトの基本的な理解(REST APIの作成経験があると望ましい)

Spring Securityとは

Spring Securityは、Spring Frameworkのサブプロジェクトとして開発されている、認証と認可を中心としたセキュリティフレームワークです。Webアプリケーションやマイクロサービスに対して、包括的なセキュリティ機能を提供します。

Spring Securityの主な機能

Spring Securityが提供する主要な機能は以下のとおりです。

機能カテゴリ 内容
認証(Authentication) ユーザー名/パスワード、OAuth2、SAML、LDAPなど多様な認証方式をサポート
認可(Authorization) URLベース、メソッドベースのアクセス制御を実現
攻撃対策 CSRF、セッション固定攻撃、クリックジャッキングなどへの防御機能を内蔵
セキュリティヘッダー X-Content-Type-Options、X-Frame-Optionsなどのレスポンスヘッダーを自動付与
セッション管理 セッションの有効期限、同時ログイン制限などを制御

認証(Authentication)と認可(Authorization)の違い

Spring Securityを理解するうえで最も重要なのは、「認証」と「認可」の概念を明確に区別することです。

認証(Authentication)とは

認証とは、「あなたは誰ですか?」という問いに答えるプロセスです。ユーザーが主張するアイデンティティを検証し、本人であることを確認します。

sequenceDiagram
    participant User as ユーザー
    participant App as アプリケーション
    participant Auth as 認証システム
    
    User->>App: ログイン要求(ID/パスワード)
    App->>Auth: 資格情報を検証
    Auth-->>App: 検証結果
    alt 認証成功
        App-->>User: 認証トークン発行
    else 認証失敗
        App-->>User: 401 Unauthorized
    end

認証の代表的な手段には以下があります。

  • ユーザー名とパスワードによる認証
  • OAuth 2.0 / OpenID Connect(Google、GitHubなどのソーシャルログイン)
  • SAML 2.0(エンタープライズシングルサインオン)
  • 証明書ベースの認証(X.509)

認可(Authorization)とは

認可とは、「あなたは何ができますか?」という問いに答えるプロセスです。認証済みのユーザーが、特定のリソースやアクションにアクセスする権限を持っているかを判定します。

flowchart LR
    A[認証済みユーザー] --> B{認可チェック}
    B -->|権限あり| C[リソースへアクセス]
    B -->|権限なし| D[403 Forbidden]

認可の実装パターンは主に以下の2種類があります。

パターン 説明
ロールベース(RBAC) ユーザーに割り当てられた役割に基づく ADMIN、USER、GUESTなど
権限ベース 個別の権限(Permission)に基づく READ_USERS、WRITE_ARTICLESなど

認証と認可の関係

認証と認可は密接に関連していますが、独立した概念です。Spring Securityでは、まず認証を行い、その後に認可を行う順序で処理が進みます。

処理 失敗時のHTTPステータス 意味
認証 401 Unauthorized 本人確認ができない
認可 403 Forbidden 権限がない

Spring SecurityのFilterChainアーキテクチャ

Spring Securityは、Servlet Filterの仕組みを活用してセキュリティ機能を実現しています。このアーキテクチャを理解することは、Spring Securityをカスタマイズする際に不可欠です。

Filterの基本構造

ServletコンテナはHTTPリクエストを処理する際、FilterChainを通じて複数のFilterを順番に実行します。

flowchart LR
    A[HTTPリクエスト] --> B[Filter 1]
    B --> C[Filter 2]
    C --> D[Filter N]
    D --> E[Servlet]
    E --> F[HTTPレスポンス]

DelegatingFilterProxy

Spring SecurityはDelegatingFilterProxyを使用して、ServletコンテナのFilterとSpringのApplicationContextを橋渡しします。これにより、Spring Beanとして定義されたFilterをServletコンテナで使用できます。

FilterChainProxy

FilterChainProxyは、Spring Securityの中核となる特別なFilterです。複数のSecurityFilterChainを管理し、リクエストURLに応じて適切なフィルターチェーンを選択します。

flowchart TB
    A[HTTPリクエスト] --> B[DelegatingFilterProxy]
    B --> C[FilterChainProxy]
    C --> D{URLパターン判定}
    D -->|/api/**| E[SecurityFilterChain 0]
    D -->|/**| F[SecurityFilterChain N]
    E --> G[認証Filter]
    G --> H[認可Filter]
    F --> I[別の設定のFilter群]

SecurityFilterChain

SecurityFilterChainは、特定のリクエストパターンに対して適用されるセキュリティフィルターのリストを定義します。Spring Boot環境では、HttpSecurityを使用して設定します。

デフォルトで有効になる主要なフィルターは以下のとおりです。

フィルター 役割
SecurityContextHolderFilter セキュリティコンテキストのライフサイクル管理
CsrfFilter CSRF攻撃からの保護
LogoutFilter ログアウト処理
UsernamePasswordAuthenticationFilter フォームログイン認証
BasicAuthenticationFilter HTTP Basic認証
AnonymousAuthenticationFilter 匿名ユーザーの認証情報設定
ExceptionTranslationFilter セキュリティ例外のHTTPレスポンスへの変換
AuthorizationFilter アクセス権限のチェック

Spring BootプロジェクトへのSpring Security導入

ここからは、実際にSpring BootプロジェクトにSpring Securityを導入する手順を解説します。

依存関係の追加

Spring Bootプロジェクトでは、spring-boot-starter-securityを追加するだけでSpring Securityが有効になります。

Mavenの場合、pom.xmlに以下を追加します。

1
2
3
4
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

Gradleの場合、build.gradleに以下を追加します。

1
implementation 'org.springframework.boot:spring-boot-starter-security'

デフォルトの動作確認

依存関係を追加してアプリケーションを起動すると、Spring Securityがデフォルト設定で有効になります。コンソールに以下のようなメッセージが出力されます。

1
Using generated security password: 8e557245-73e2-4286-969a-ff57fe326336

この状態でhttp://localhost:8080にアクセスすると、Spring Securityが提供するデフォルトのログインページにリダイレクトされます。

デフォルトの認証情報は以下のとおりです。

項目
ユーザー名 user
パスワード コンソールに出力された値

デフォルト設定の挙動

Spring Boot + Spring Securityのデフォルト設定では、以下の動作が自動的に有効になります。

  • すべてのエンドポイントに対して認証を要求
  • フォームベースのログイン/ログアウト機能を提供
  • HTTP Basic認証をサポート
  • CSRF保護を有効化
  • セッション固定攻撃対策を有効化
  • セキュリティ関連のHTTPヘッダーを自動付与
  • デフォルトユーザー(user)を自動生成

SecurityFilterChainのカスタマイズ

デフォルト設定を変更するには、SecurityFilterChainをBeanとして定義します。

基本的なSecurityFilterChain設定

以下は、最小構成のカスタム設定例です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(authorize -> authorize
                .requestMatchers("/public/**").permitAll()
                .requestMatchers("/admin/**").hasRole("ADMIN")
                .anyRequest().authenticated()
            )
            .formLogin(form -> form
                .loginPage("/login")
                .permitAll()
            )
            .logout(logout -> logout
                .permitAll()
            );

        return http.build();
    }
}

設定項目の解説

上記の設定コードの各部分について説明します。

設定 説明
@EnableWebSecurity Spring Securityの設定を有効化するアノテーション
authorizeHttpRequests() URLベースの認可ルールを定義
requestMatchers() 特定のURLパターンに対するルールを設定
permitAll() 認証なしでアクセスを許可
hasRole() 指定したロールを持つユーザーのみアクセスを許可
authenticated() 認証済みユーザーのみアクセスを許可
formLogin() フォームベースログインの設定
logout() ログアウト機能の設定

REST API向けの設定

REST APIを構築する場合、フォームログインの代わりにHTTP Basic認証やJWT認証を使用することが一般的です。以下は、REST API向けの最小設定例です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class ApiSecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .csrf(csrf -> csrf.disable())
            .sessionManagement(session -> session
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            )
            .authorizeHttpRequests(authorize -> authorize
                .requestMatchers("/api/public/**").permitAll()
                .anyRequest().authenticated()
            )
            .httpBasic(Customizer.withDefaults());

        return http.build();
    }
}

この設定では以下の変更を行っています。

設定 理由
CSRF無効化 ステートレスなREST APIではCSRFトークンの管理が困難なため
セッション無効化 REST APIはステートレスであるべきため
HTTP Basic認証 シンプルな認証方式として採用

インメモリユーザーの設定

開発・テスト目的で、メモリ上にユーザー情報を定義することができます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

@Configuration
public class UserConfig {

    @Bean
    public UserDetailsService userDetailsService() {
        UserDetails user = User.builder()
            .username("user")
            .password(passwordEncoder().encode("password"))
            .roles("USER")
            .build();

        UserDetails admin = User.builder()
            .username("admin")
            .password(passwordEncoder().encode("admin123"))
            .roles("ADMIN", "USER")
            .build();

        return new InMemoryUserDetailsManager(user, admin);
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

PasswordEncoderの重要性

Spring Security 5以降では、パスワードは必ずエンコード(ハッシュ化)して保存する必要があります。平文パスワードを使用すると、起動時にエラーが発生します。

推奨されるエンコーダーはBCryptPasswordEncoderです。BCryptは以下の特徴を持ちます。

  • ソルト(Salt)を自動生成してレインボーテーブル攻撃を防止
  • コストファクターによる計算量調整が可能
  • 将来のハードウェア性能向上に対応可能

動作確認

設定が完了したら、以下の手順で動作を確認します。

アプリケーションの起動

1
./mvnw spring-boot:run

認証なしでのアクセステスト

1
curl -i http://localhost:8080/api/private

期待される結果は401 Unauthorizedです。

認証ありでのアクセステスト

1
curl -i -u user:password http://localhost:8080/api/private

期待される結果は200 OKまたは対応するレスポンスです。

デバッグとトラブルシューティング

Spring Securityの動作を詳細に確認するには、ログレベルを変更します。

application.propertiesに以下を追加します。

1
logging.level.org.springframework.security=DEBUG

これにより、各フィルターの実行順序や認証・認可の判定結果がログに出力されます。

1
2
3
4
5
DEBUG FilterChainProxy : Securing GET /api/users
TRACE FilterChainProxy : Invoking SecurityContextHolderFilter (1/15)
TRACE FilterChainProxy : Invoking HeaderWriterFilter (2/15)
...
DEBUG AuthorizationFilter : Authorized request

まとめ

本記事では、Spring Securityの基本概念と、Spring Bootプロジェクトへの導入方法を解説しました。

要点を整理すると以下のとおりです。

  • 認証は「誰であるか」を確認し、認可は「何ができるか」を判定する
  • Spring SecurityはSecurityFilterChainを通じてリクエストを処理する
  • Spring Bootではspring-boot-starter-securityを追加するだけでセキュリティが有効化される
  • SecurityFilterChain Beanを定義することでカスタマイズが可能
  • パスワードは必ずPasswordEncoderでエンコードする

次のステップとして、以下のトピックの学習を推奨します。

  • UserDetailsServiceによるデータベース認証の実装
  • JWT(JSON Web Token)を使用したステートレス認証
  • OAuth 2.0 / OpenID Connectによるソーシャルログイン
  • メソッドレベルセキュリティ(@PreAuthorize@Secured

参考リンク