はじめに
Springbootを使ってるけど、SpringSecurityっていったいなんなの?設定は行ったけど、裏でどんな風に動いてるの?というのをまとめてみたいと思います。
参考
SpringSecurity公式ドキュメント
プロになるためのSpring入門ーーゼロからの開発力養成講座
Spring Securityとは
公式ドキュメントより
Spring Security は、認証、認可、一般的な攻撃に対する保護を提供するフレームワークです。命令型およびリアクティブ両方のアプリケーションに対するファーストクラスのサポートにより、Spring ベースのアプリケーションを保護するためのデファクトスタンダードとなっています。
つまり、なにかすごいことをしてくれているフレームワークです。。。
もう少し簡単に要約すると、
- Spring Security(スプリング・セキュリティ)は、JavaのSpringベースのアプリケーションに対して、認証(Authentication)と認可(Authorization)をはじめとする、様々なセキュリティ機能を提供するフレームワークです。
つまり
Spring Securityとは「門番システム」です
Spring Securityの役割は、Webアプリケーションを「安全な建物」にするための「門番(セキュリティシステム)」を提供することです。
主な機能
Spring Securityの主な機能は以下の2点に集約されます。
1.認証 (Authentication)
-
ユーザーが「誰であるか」を確認するプロセスです。
-
ユーザー名とパスワードの検証などを行い、正当なユーザーであることを確認します。(ユーザーが誰か、身分証で確認する)
-
様々な認証方法(フォーム認証、LDAP、OAuth 2.0など)に対応しています
2. 認可 (Authorization)
-
認証されたユーザーが「何にアクセスする権限を持っているか」を制御するプロセスです。
-
特定のURLパス、メソッド、またはリソースへのアクセスを、ユーザーの役割(ロール)や権限に基づいて許可または拒否します。(ユーザーがどの部屋に入る許可を持っているか確認する。)
その他のセキュリティ対策
認証・認可の他にも、Webアプリケーションの一般的な脆弱性から保護するための機能を提供します。
-
CSRF(クロスサイトリクエストフォージェリ)対策
-
セッション固定攻撃対策
-
セキュリティヘッダーの出力(例: X-Frame-Options, HSTS
仕組みの概要
Spring Securityは、Webアプリケーションへのリクエストを処理する過程で、サーブレットフィルターの仕組みを利用してセキュリティ機能を実現しています。これにより、アプリケーションのビジネスロジックを変更することなく、宣言的にセキュリティルールを適用し、柔軟かつ拡張性の高いセキュリティ対策を実装できます。
仕組みは少しだけわかったけど、実際Spring Securityを動かすためにはどうすればいいの?専用のコントローラとかいるの?
ログイン認証を動かすために自分で決められたコントローラを作成してそこに飛ばす必要はありません。
Spring Securityを使う場合、通常は特定の条件を満たすHTMLフォームと設定を行うだけで、Spring Securityの内部フィルターが自動的にリクエストを処理し、認証ロジックを実行します
Spring Securityでのログイン認証の動作
ログイン認証を機能させるために、Webアプリケーション側で必要なことは主に以下の3点です。
1. ログインフォームの作成 (HTML)
Spring Securityのデフォルト設定を利用する場合、ログインフォームのHTMLは以下のルールに従って作成する必要があります。
-
アクションURL:/login (POSTメソッド)
このURLにフォームを送信すると、Spring Securityの認証フィルターが自動的にリクエストをキャッチします。自分で/loginに対応するコントローラを定義する必要はありません。 -
ユーザー名フィールド:
username
ユーザー名(ID)を入力する要素のname属性をusernameにする必要があります。 -
パスワードフィールド:
password
パスワードを入力する<input>要素のname属性をpasswordにする必要があります。
HTMLフォームの例
<form method="POST" action="/login">
<label for="username">ユーザー名:</label>
<input type="text" id="username" name="username"><br>
<label for="password">パスワード:</label>
<input type="password" id="password" name="password"><br>
<button type="submit">ログイン</button>
</form>
2. 認証ロジックの実装 (UserDetailsService)
フォームからの入力(ユーザー名)を受け取り、データベースからユーザー情報とパスワードを取得して、Spring Securityに渡すためのUserDetailsServiceの実装が必要です。
3. Spring Securityの設定 (SecurityFilterChain)
Spring Securityの設定クラス(SecurityConfigなど)で、フォームログインを有効にする設定を記述します。
// Spring Boot 3 以降の簡潔な設定例
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
// 認可設定(アクセス制限)
// ...
// フォームログインを有効化(デフォルト設定を使用)
.formLogin(Customizer.withDefaults()); // ← これだけでSpring Securityがログインフォーム処理を制御します。
return http.build();
}
この設定を行うだけで、Spring Securityの内部にあるUsernamePasswordAuthenticationFilterというフィルターが/loginへのPOSTリクエストを自動的にインターセプトし、認証処理を実行します。そのため、専用のログイン処理コントローラを作る必要がありません。DI注入の技術を使用して、内部的に認証を行ってくれています。
Spring FrameworkのDIコンテナってなに?メリットは?
具体的な仕組み
Spring Securityの全ての機能は、「サーブレットフィルター」という仕組みで動いています。クライアントからのリクエストがWebアプリケーションの処理にたどり着く前に、たくさんのセキュリティの関所(フィルター)を順番に通過します。この関所の連なりをフィルターチェーンと呼びます
1. フィルターチェーンの図解
クライアントからのリクエストは、まずSpring Securityの多数のフィルターを順番に通過します。

特に重要なフィルターが、認証を行うフィルターと、認可を行うフィルターです。
リクエストは、まず認証フィルターで「誰か」を確認され、次に認可フィルターで「アクセス権があるか」を確認されてから、ようやくビジネスロジック(Controller)に到達します。
2. 認証の仕組みとコード(誰であるかの証明) (UserDetailsService)
認証の裏側の流れ(UserDetailsServiceの役割)
認証は、主にユーザー情報を取得するUserDetailsServiceというインターフェースを実装して行います
ログイン処理が実行されると、Spring Securityは以下の裏側の動きでユーザーを認証します。
-
リクエストキャッチ:
UsernamePasswordAuthenticationFilterが、フォームから送信されたIDとパスワードをキャッチします。 -
情報取得の依頼: フィルターは、
UserDetailsServiceというコンポーネントに「このIDのユーザー情報をデータベースから取ってきて」と依頼します。 -
情報取得と返却: あなたが実装した
UserDetailsServiceがDBなどを参照し、ユーザーの情報(ID、ハッシュ化されたパスワード、権限)を返します。
(UserDetailsServiceがUserDetailsオブジェクトを返した後、裏側では以下の手順で「認証」が完了します) -
パスワード比較: フィルターは、入力されたパスワードと、DBから取得したパスワードを比較し、一致すれば認証成功となります。
(UserDetailsServiceは、Spring Security内部のDaoAuthenticationProviderというコンポーネントに呼び出されています。このプロバイダーが、実際の認証(パスワードの比較)を行います。)
認証に必要なコード:UserDetailsServiceの実装
データベースやインメモリからユーザー情報を取得する役割を担います
@Service
public class CustomUserDetailsService implements UserDetailsService {
// ユーザー情報(実際はDBから取得する)
private final Map<String, String> users = Map.of(
"taro", "{bcrypt}$2a$10$wTf7L...", // DBに保存されているハッシュ化されたパスワード
"jiro", "{bcrypt}$2a$10$xyz4V..." // bcrypt方式でエンコードされていることを示すプレフィックス
);
/**
* ログインIDを元にユーザー情報を取得し、UserDetailsオブジェクトを返却する
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
if (!users.containsKey(username)) {
// ユーザーが見つからなければ例外をスロー
throw new UsernameNotFoundException("ユーザー名が見つかりません: " + username);
}
// 取得した情報からUserDetailsオブジェクトを生成
return User.withUsername(username)
.password(users.get(username))
.roles(getRoles(username)) // ロール(権限)を設定
.build();
}
// ユーザー名に対応するロールを返す(ここでは例としてハードコード)
private String[] getRoles(String username) {
if ("taro".equals(username)) {
return new String[]{"ADMIN", "USER"};
} else {
return new String[]{"USER"};
}
}
}
認証済みオブジェクトの生成
認証プロバイダーは、認証が成功すると、以下の情報を含むAuthenticationというオブジェクトを生成します。
- プリンシパル (Principal): 認証済みのユーザー情報
- 権限 (Authorities):
getAuthorities()から取得したロール(ROLE_ADMINなど)。 - 認証済みフラグ:
isAuthenticated()がtrueになります。
セキュリティコンテキストへの保存
最後に、認証プロバイダーはこのAuthenticationオブジェクトをSecurityContextHolderという静的な場所に保存します。
SecurityContextHolder→SecurityContext→Authentication Object (認証済み)
このSecurityContextHolderに認証情報が保存されることで、その後の全てのリクエストで、Spring Securityは「このユーザーはログイン済みであり、ROLE_ADMINの権限を持っている」ことを迅速に確認できるようになります。
認可の仕組みとコード(何ができるかの制限)(設定)
認可の裏側の流れ(FilterSecurityInterceptorの役割)
認証が成功した後、ユーザーが特定のURLにアクセスしようとすると、以下の裏側の動きでアクセスが許可されるかがチェックされます。
-
権限チェックの実行: フィルターチェーンの終盤にある
FilterSecurityInterceptor(認可フィルター)が起動します。 -
設定を参照: 認可フィルターは、あなたが定義したSpring Securityの設定(SecurityFilterChain)を参照します。
-
アクセス可否の判断: 「このURLにアクセスするには、ADMINというロールが必要だ」という設定と、ユーザーが持っているロールを照合します。
-
ロールを持っていれば → アクセス許可(アプリケーション本体へ処理が渡る)
-
ロールを持っていなければ → アクセス拒否(403 Forbiddenエラーを返す)
-
認可に必要なコード:SecurityFilterChainの設定
どのURLにどのようなアクセス権限(ロール)が必要かを定義します。
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
// 1. 認可リクエストの設定
.authorizeHttpRequests(authorize -> authorize
// /admin/以下のURLへのアクセスは、"ADMIN" ロールを持つユーザーのみ許可
.requestMatchers("/admin/**").hasRole("ADMIN")
// /user/以下のURLへのアクセスは、"USER" ロールを持つユーザーのみ許可
.requestMatchers("/user/**").hasRole("USER")
// /public/とトップページ("/")は、誰でもアクセスを許可 (permitAll)
.requestMatchers("/", "/public/**").permitAll()
// それ以外の全てのリクエストは、認証済みであること(ログイン済み)を要求
.anyRequest().authenticated()
)
// 2. ログインフォームの設定 (デフォルトのログインページを使用)
.formLogin(Customizer.withDefaults())
// 3. ログアウトの設定
.logout(logout -> logout
.logoutSuccessUrl("/") // ログアウト成功後に遷移するページ
.permitAll()
);
return http.build();
}
}
補足: 役割 (Role) について
Spring Securityでは、アクセス権限を管理するためにロール(役割)という概念を使います。上記の例で、hasRole("ADMIN")と設定した場合、認証済みのユーザーがROLE_ADMINという権限を持っているかをチェックします。USERクラスで権限を設定する際は、oles("USER")のようにROLE_を省略しても、Spring Securityが内部で自動的に付与してくれます。
終わりに
Spring Securityはアプリケーションが立ち上がったときにDI注入される仕組みを利用し、アプリ実装者は3つのことだけを意識すれば強固なセキュリティ機能を使用できる、便利フレームワークです。
補足:
pring Securityは、設定クラスやUserDetailsServiceなどのコンポーネントを@Beanや@ServiceとしてDIコンテナに登録し、それらを連携させて動作します。これはSpringフレームワークの基本です。
