Spring Securityは、Springで作るアプリケーションに認証(誰がアクセスしているかの確認)と認可(その人に何を許すかの判定)、CSRFなどの一般的な攻撃への対策を加えるフレームワークです。公式リファレンスは「authentication, authorization, and protection against common attacks」を提供するものと定義しています。2026年10月時点の最新安定版は7.1.1で、Spring Boot 4.1.1が標準で取り込む版です。この記事では、Spring Boot 4.1.1・JDK 25で実際に起動して確かめた挙動をもとに、仕組みと基本設定、7系で変わった点を説明します。
まとめ:Spring Securityの役割と最初に押さえる5点
- 役割:認証・認可・攻撃対策の3つ。ログイン画面、権限による画面やAPIの出し分け、CSRF対策、セキュリティヘッダーの付与をまとめて担う。
- 仕組み:アプリ本体より前に並ぶ「フィルタの列」(SecurityFilterChain)がリクエストを検査する。コントローラーに届く前に401や403で止める。
- 依存を足すだけで有効:Spring Bootでは
spring-boot-starter-securityを追加した時点で全URLが認証必須になり、ログ出力されたパスワードでしか入れない。 - 設定の書き方:
SecurityFilterChainのBeanをラムダ式で書く。WebSecurityConfigurerAdapterやand()を使う旧記事のコードは7系では動かない。 - 版:最新は7.1.1。6.5系の無償サポートは2026年6月30日に終了し、7.0系も2026年12月31日に終わる。新規は7.1系(Spring Boot 4.1系)で始める。
Spring Securityの役割:認証・認可・攻撃対策の3本柱
読み方は「スプリング・セキュリティ」です。Springプロジェクトの一つで、Webアプリ(サーブレット)とリアクティブ(WebFlux)の両方に対応します。Spring Boot自体はセキュリティ機能を持たず、Spring Securityを自動設定で組み込む役回りです。Bootが「既定の設定」を用意し、細かな規則はSpring Securityの設定で書く、と分担を覚えておくと混乱しません。
APIをBasic認証などで呼んだ場合、認証と認可は失敗したときのHTTPステータスが違います。この違いを押さえておくと、エラーの切り分けが速くなります。ブラウザからのフォームログインでは、未認証は401ではなくログイン画面への302転送になります。
| 機能 | 問い | 失敗時 | 代表的な部品 |
|---|---|---|---|
| 認証 | あなたは誰か | 401(ブラウザは302) | AuthenticationManager |
| 認可 | それをしてよいか | 403 Forbidden | AuthorizationManager |
| 攻撃対策 | 正規の画面からの操作か | リクエストを拒否 | CsrfFilter・HeaderWriterFilter |
2025年11月の7.0では、OAuth 2.0の認可サーバーを作るSpring Authorization ServerがSpring Security本体に取り込まれ、多要素認証(MFA)の仕組みも加わりました。トークンを発行する側と検証する側の両方を同じプロジェクトで扱えるようになっています。
リクエストがSpring Securityを通る仕組み
FilterChainProxyとSecurityFilterChainの関係
Spring Securityの入口としてサーブレットコンテナに登録されるのは、DelegatingFilterProxy という1本のフィルタです。これがSpringのBeanである FilterChainProxy に処理を渡し、FilterChainProxy はURLに合う SecurityFilterChain を1つ選んで、その中のフィルタを順に実行します。Spring Boot 4.1.1の既定構成では16本のフィルタが並び、CSRFの検査、ログイン処理、例外の変換、最後の認可判定までがこの列の中で終わります。各フィルタの順序と差し込み方はSecurityFilterChainとは?Spring Securityのフィルタ実行順序と設定例で実測しています。
認証の部品:AuthenticationManager・UserDetailsService・PasswordEncoder
ログイン処理のフィルタは、受け取ったユーザー名とパスワードを AuthenticationManager に渡します。標準の実装 ProviderManager は、登録された AuthenticationProvider に順に確認を頼みます。ID・パスワード方式の場合は DaoAuthenticationProvider が UserDetailsService でユーザーを読み出し、PasswordEncoder でハッシュ値を照合します。
PasswordEncoder は spring-security-crypto モジュールにあります。PasswordEncoderFactories.createDelegatingPasswordEncoder() で作ると、BCryptでハッシュ化し、方式名を接頭辞として付けます。7.1.1で実行した出力は次のとおりです。
{bcrypt}$2a$10$rqs..pkDunT234Yj1dAvPet1jSATG95WD82DeGHNPQYUH2nJC.ag2
接頭辞があるので、将来ほかの方式へ移っても古いハッシュを照合できます。逆に、接頭辞の無い値を照合しようとすると IllegalArgumentException(「each password must have a password encoding prefix」)で止まります。平文を保存していた旧システムから移行するときにまず当たるエラーです。独自の AuthenticationProvider を作る手順はSpring Bootの認証実装:カスタムAuthenticationProviderの作り方とテストにまとめています。
認証結果の置き場:SecurityContextHolder
認証に成功すると、結果の Authentication が SecurityContextHolder に入ります。コントローラーやサービスからは次のように取り出せます。
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
return auth.getName() + " " + auth.getAuthorities();
7.1.1で、ロールUSERのユーザーaliceがパスワードでログインした状態で実行すると、権限一覧は [ROLE_USER, FactorGrantedAuthority [authority=FACTOR_PASSWORD, ...]] になりました。7系では、どの方法で本人確認したかを示す FactorGrantedAuthority が認証時に自動で追加されます。多要素認証はこの値を使って「パスワードとワンタイムトークンの両方が済んでいるか」を判定します。権限一覧を文字列で比べている既存コードは、7系に上げると要素が1つ増える点に注意してください。
認可の部品:AuthorizationManager(AccessDecisionManagerの後継)
アクセスを許すかどうかは AuthorizationManager が判定します。公式ドキュメントは AuthorizationManagerが「AccessDecisionManagerとAccessDecisionVoterの両方を置き換える」と説明しています。独自の判定は、次のようにラムダ1つで書けます(例はログイン済みの利用者に日本時間の9時以上18時未満だけ許可する規則)。
AuthorizationManager<RequestAuthorizationContext> officeHours() {
return (authentication, context) -> {
int hour = java.time.LocalTime.now(java.time.ZoneId.of("Asia/Tokyo")).getHour();
boolean loggedIn = new AuthenticationTrustResolverImpl().isAuthenticated(authentication.get());
boolean allowed = loggedIn && hour >= 9 && hour < 18;
return new AuthorizationDecision(allowed);
};
}
未ログインの利用者にも匿名の Authentication が入っており、その isAuthenticated() は true を返します。authentication.get().isAuthenticated() だけで判定すると匿名でも通ってしまうため、AuthenticationTrustResolverImpl で匿名を除外しています。7.1.1で確かめると、未ログインは401、aliceは200でした。LocalTime.now() はサーバーのタイムゾーンに従うので、UTCで動くコンテナでは時刻帯がずれます。タイムゾーンは明示してください。
古い解説にある AccessDecisionManager や AccessDecisionVoter は、7.0で spring-security-access という別モジュールに移されました。Spring Boot 4.1.1のスターターが取り込むjarは config・core・crypto・web の4つで、実際にビルドした成果物にも AccessDecisionManager は入っていませんでした。旧方式のコードをそのまま貼るとコンパイルできません。
依存を追加しただけで起きること(Spring Boot 4.1.1で実測)
設定を何も書かずに、/hello を返すだけのアプリへ spring-boot-starter-security を加えて起動しました。起動ログには次の2行が出ます。
Using generated security password: d9fce44d-e604-4ab3-9509-e1449d0db1f6
This generated password is for development use only. Your security configuration must be updated before running your application in production.
ユーザー名は user 固定で、パスワードは起動のたびに変わります。Spring Bootのドキュメントどおり、spring.security.user.name と spring.security.user.password で固定値に変えられます。この状態でcurlから叩いた結果が次の表です。
| リクエスト | 結果 |
|---|---|
| GET /hello(Acceptヘッダーなし) | 401・WWW-Authenticate: Basic |
| GET /hello(Accept: text/html) | 302で /login へ転送 |
| GET /login | 200・ログイン画面(_csrf入り) |
| GET /hello(Basic認証 user) | 200 |
| GET /hello(パスワード誤り) | 401 |
| POST(Basic認証・CSRFトークンなし) | 401 |
ブラウザからはログイン画面、APIクライアントからはBasic認証と、Acceptヘッダーで応答が分かれます。CSRFトークンの無いPOSTが403でなく401になるのは、この既定構成ではCSRFの検査がBasic認証のフィルタより先に動くためです。拒否された時点ではまだ認証されておらず、そのあとのエラー画面(/error)への転送でもBasic認証は実行されないので、匿名のまま認可に失敗して401になります(仕組みは7001の記事で追っています)。どの応答にも X-Frame-Options: DENY、X-Content-Type-Options: nosniff、Cache-Control: no-cache, no-store, max-age=0, must-revalidate が付きました。HTTPで起動したためHSTSヘッダーは付いていません。
この既定構成は開発用です。SecurityFilterChain のBeanを定義するとBootの既定のフィルタ構成は外れますが、生成パスワードのユーザーは残ります。UserDetailsService のBeanを定義すると、生成パスワードのログは出なくなりました(次の章の設定で確認)。
基本設定:SecurityFilterChainでのURL別の認可規則
Mavenなら依存は1つです。Spring Boot 4.1.1の親POMを使えば版の指定は要りません。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
次の設定は、/public/** を公開、/admin/** をADMINロール限定、それ以外をログイン必須にし、メモリ上に2人のユーザーを作ります。
@Configuration
@EnableMethodSecurity(securedEnabled = true)
public class SecurityConfig {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/**").permitAll()
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated())
.formLogin(Customizer.withDefaults())
.httpBasic(Customizer.withDefaults());
return http.build();
}
@Bean
PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
@Bean
UserDetailsService users(PasswordEncoder encoder) {
return new InMemoryUserDetailsManager(
User.withUsername("alice").password(encoder.encode("alice-pass")).roles("USER").build(),
User.withUsername("bob").password(encoder.encode("bob-pass")).roles("USER", "ADMIN").build());
}
}
alice・bobのパスワードは動作確認用の固定値で、本番ではDBやIDプロバイダーから読み込みます。/public/hello・/me・/admin/panel を返すだけのコントローラーを置き、起動してBasic認証で叩いた結果です。
| URL | 未ログイン | alice(USER) | bob(USER・ADMIN) |
|---|---|---|---|
| /public/hello | 200 | 200 | 200 |
| /me | 401 | 200 | 200 |
| /admin/panel | 401 | 403 | 200 |
ログインしていなければ401、ログイン済みでも権限が足りなければ403です。hasRole("ADMIN") は権限 ROLE_ADMIN を探すので、DBに権限を保存するときは接頭辞 ROLE_ を付けるか、hasAuthority を使います。規則は上から順に評価され、最初に一致したものが使われます。anyRequest() を先頭に書くと、起動時に IllegalStateException: Can't configure requestMatchers after anyRequest で止まります。
メソッド単位の認可:@PreAuthorizeと@Secured
URLではなくメソッドに条件を付けるには @EnableMethodSecurity を付け、@PreAuthorize("hasRole('ADMIN')") などを書きます。ここで間違えやすいのが @Secured です。@EnableMethodSecurity の既定は prePostEnabled だけが有効で、securedEnabled と jsr250Enabled は明示的に有効化が必要です。
| 設定 | @PreAuthorize(ADMIN限定) | @Secured(“ROLE_ADMIN”) |
|---|---|---|
| @EnableMethodSecurity | aliceは403 | aliceも200 |
| @EnableMethodSecurity(securedEnabled = true) | aliceは403 | aliceは403 |
エラーも警告も出ずに素通りするので、テストで権限の無いユーザーを流さない限り気づけません。公式も @PreAuthorize の方が表現力が高いとして推奨しています。新しく書くなら @PreAuthorize に揃え、既存の @Secured が残っているプロジェクトでは securedEnabled = true が付いているかを最初に確認してください。
Spring Security 7で動かなくなった旧コード
「Spring Security 入門」で検索して出てくる解説には、5系の書き方がまだ多く残っています。7系で使えないものを並べます。
| 旧い書き方 | 変わった版 | 今の書き方 |
|---|---|---|
| extends WebSecurityConfigurerAdapter | 6.0で削除 | SecurityFilterChainのBean |
| antMatchers() / mvcMatchers() | 6.0で削除 | requestMatchers() |
| authorizeRequests() | 7.0で削除 | authorizeHttpRequests() |
| .and() でつなぐ設定 | 7.0で削除 | ラムダ式(Customizer) |
| AccessDecisionManager / Voter | 7.0で別モジュールへ | AuthorizationManager |
| AuthorizationManager#check | 7.0で削除 | AuthorizationManager#authorize |
| javax.servlet.* | 6.0から | jakarta.servlet.* |
出典は7.0のWhat’s Newと、WebSecurityConfigurerAdapter の削除を記した6.0.0-RC1のリリースノートです。コピーしたコードがコンパイルできないときは、まずこの表の左列が含まれていないかを見てください。Spring Boot側の変更はSpring Boot 4とは?最新バージョン4.1の変更点・新機能とSpring Boot 3との違いで扱っています。
バージョンとサポート期限:6.5系を使い続けるかの判断
Springのサポート表(api.spring.io のgenerations)で2026年10月18日に確認した値です。最新パッチはMaven Centralで確認しました。
| 系列 | 対応Boot | 最新パッチ | 無償サポート終了 | 商用サポート終了 |
|---|---|---|---|---|
| 7.2.x | 4.2.x | 7.2.0-M2(開発版) | 2027-12-31 | 2028-12-31 |
| 7.1.x | 4.1.x | 7.1.1 | 2027-07-31 | 2028-07-31 |
| 7.0.x | 4.0.x | 7.0.7 | 2026-12-31 | 2027-12-31 |
| 6.5.x | 3.5.x | 6.5.11(無償の最終) | 2026-06-30 | 2032-06-30 |
6.5系の無償サポート終了は実害として表れています。2026年8月20日に公表されたCVE-2026-41707(DPoPの使い回し防止キャッシュを大量の要求で押し出し、盗んだ証明を再利用できる脆弱性)は、7.1.0・7.0.0〜7.0.6・6.5.0〜6.5.11が影響を受けます。修正版は7.1.1と7.0.7が無償で出ましたが、6.5系の修正版6.5.12は商用サポート契約者向けだけです。DPoPを使っていなければこのCVEの影響はありません。ただし、今後見つかる脆弱性でも6.5系の修正版は商用サポート向けにしか出ない見込みです。商用サポートを契約しないなら、Spring Boot 3.5の運用は移行までのつなぎと位置づけ、4.1系(Spring Security 7.1系)への移行計画を立ててください。Bootの版ごとの期限はSpring Bootバージョン一覧とサポート期限(EOL)にまとめています。
採用すべき場面と、Spring Securityだけで済ませない方がよい場面
Spring BootでWebアプリやAPIを作るなら、認証・認可は自作せずSpring Securityを使うべきです。セッション固定攻撃への対策、CSRFトークン、セキュリティヘッダーは既定で有効になり、パスワードのハッシュ化も PasswordEncoder を登録・変更処理に組み込むだけで済みます。自作すると漏れやすい部分ほど既製の実装があります。
一方、ユーザー登録、パスワード再設定、多要素認証の登録画面、退会といったID管理の業務フローは、Spring Securityが用意するのは部品までで、画面と手順は自分で作る範囲です。社内外の複数システムで同じアカウントを使うなら、Keycloakや Microsoft Entra ID などのIDプロバイダーにID管理を任せ、Spring Security側はOpenID Connectのログインやトークン検証(リソースサーバー)に絞る構成の方が保守の負担が小さくなります。トークンの検証方式はJWTとは?構造・署名検証の仕組みとセッション・OAuth/OIDCとの違い、ヘッダーの書き方はBearer Token(ベアラートークン)とは?で解説しています。認証基盤を含めた設計から相談したい場合は、一爽の認証基盤・ID管理システム開発で対応しています。
よくある質問
Spring BootとSpring Securityの違いは何ですか?
Spring Bootはアプリの起動と設定を自動化する仕組みで、セキュリティ機能そのものは持っていません。Spring Securityが認証・認可を実装し、Spring Bootはそれを既定の設定(全URL認証必須・生成パスワードのユーザー)で組み込みます。
Spring Securityの最新バージョンはいくつですか?
2026年10月18日時点の最新安定版は7.1.1(Spring Boot 4.1.1が採用)です。7.2.0は開発版(M2)で、Springのサポート表では7.2系の初回リリースが2026年11月とされています。
CSRF対策は無効にしてよいですか?
ブラウザから使われる画面やAPIでは無効にしないでください。公式ドキュメントは、ブラウザ以外のクライアントだけが使うサービスなら無効化してよいとしています。「Invalid CSRF token」などのエラーの直し方はInvalid CSRF tokenの意味と直し方を参照してください。
CORSの設定はどこに書きますか?
SecurityFilterChainで http.cors(Customizer.withDefaults()) を有効にし、CorsConfigurationSource のBeanで許可するオリジンを定義します。プリフライト要求にはCookieが付かないため、Spring Securityより前にCORSを処理しないと認証エラーで弾かれます。ReactなどのSPAと組み合わせる手順はSpring BootとReactの連携方法で実測しています。
SecurityFilterChainは何本まで定義できますか?
本数の上限はなく、securityMatcher で対象URLを分けて複数定義できます。先に一致したチェーンだけが使われるので、@Order で狭い範囲のチェーンを先に置きます。
関連記事
- SecurityFilterChainとは?Spring Securityのフィルタ実行順序と設定例【Spring Security 7.1対応】
- Spring Bootの認証実装:カスタムAuthenticationProviderの作り方とテスト【Spring Security 7.1対応】
- Spring BootとReactの連携方法|REST API・CORS・JWT認証から1つのjarへの同梱まで【Spring Boot 4.1】
- Invalid CSRF tokenの意味と直し方|Laravel 419・Django・Spring Securityのエラーを切り分ける
- JWTとは?構造・署名検証の仕組みとセッション・OAuth/OIDCとの違いを実装視点で解説