Spring Security 7移行ガイド:SecurityFilterChainはどう変わる?
はじめに
Spring Boot / Spring Security を使っているプロジェクトでは、バージョンアップのたびに「セキュリティ設定の書き方が変わっていてビルドが通らない」「古い記事のコードをそのまま使えない」といった問題に遭遇しがちです。
特に Spring Security 7 への移行では、SecurityFilterChain そのものの考え方が大きく変わるというより、SecurityFilterChain を定義するときの書き方がより明確に整理される、という理解が近いです。
Spring Security 7 では、従来のチェーン形式の設定ではなく Lambda DSL を使った設定が前提になります。公式ドキュメントでも、Spring Security 7 では Lambda DSL の利用が必須になると説明されています。
この記事では、Spring Security 6.x から 7 へ移行する人に向けて、SecurityFilterChain の書き方がどう変わるのか、既存コードをどう直せばよいのかを実装例付きで解説します。
SecurityFilterChainはなくならない
最初に安心してほしいのですが、Spring Security 7 でも SecurityFilterChain は引き続き使います。
変わるのは主に以下です。
- Lambda DSL が必須になる
- .and() でつなぐ古い書き方をやめる
- authorizeRequests ではなく authorizeHttpRequests を使う
- apply() ではなく with() を使う
- リクエストマッチャー周りの扱いが変わる
- AntPathRequestMatcher / MvcRequestMatcher 前提のコードを見直す
SecurityFilterChain は、Spring Security が「このリクエストにはどのセキュリティフィルター群を適用するか」を決めるための設定です。公式ドキュメントでも、SecurityFilterChain は FilterChainProxy によって現在のリクエストに適用する Security Filter を決定するために使われる、と説明されています。
つまり、Spring Security 7 でも基本構造は次のままです。
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
// ここにセキュリティ設定を書く
;
return http.build();
}ただし、中身の書き方を Spring Security 7 向けに整える必要があります。
移行のおすすめ順序
いきなり Spring Security 7 に上げるより、まずは Spring Security 6.5 系で警告や非推奨設定を潰してから 7 に上げるのがおすすめです。
公式の移行ガイドでも、Spring Security 6.5 は 6.x 世代の最後のリリースであり、7.0 方式に備えるための準備手順が用意されているため、まず 6.5 と準備手順を使うことが推奨されています。
おすすめの流れは以下です。
- Spring Boot / Spring Security 6.5系へ上げる
- 非推奨APIや警告を解消する
- SecurityFilterChainをLambda DSLへ寄せる
- requestMatchersやCustom DSLを見直す
- テストで認可・認証の挙動を確認する
- Spring Security 7へ上げる
移行作業で一番つらいのは、バージョンアップ後に大量のコンパイルエラーと認可エラーが同時に出ることです。
まず 6.5 系で「7 に近い書き方」に直しておくと、かなり安全に移行できます。
変更点1:Lambda DSLが必須になる
Spring Security 7 移行で最も重要なのが Lambda DSL です。
古い書き方
Spring Security 5.x や 6.x 初期の記事では、以下のような .and() でつなぐ書き方をよく見かけます。
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests()
.requestMatchers("/public/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.logoutUrl("/logout");
return http.build();
}
}Spring Security 7 では、このようなチェーン形式ではなく Lambda DSL に寄せます。
Spring Security 7向けの書き方
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/public/**").permitAll()
.anyRequest().authenticated()
)
.formLogin(form -> form
.loginPage("/login")
.permitAll()
)
.logout(logout -> logout
.logoutUrl("/logout")
);
return http.build();
}
}ポイントは .and() を使わないことです。
Lambda DSL では、それぞれの設定ブロックがラムダ式の中で完結します。authorizeHttpRequests、formLogin、logout などがそれぞれ独立して見えるので、設定の見通しがよくなります。
変更点2:Customizer.withDefaults()を使う場面が増える
デフォルト設定で有効化したい場合は、Customizer.withDefaults() を使います。
例えば HTTP Basic 認証をデフォルト設定で有効化する場合は以下です。
import static org.springframework.security.config.Customizer.withDefaults;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize -> authorize
.anyRequest().authenticated()
)
.httpBasic(withDefaults());
return http.build();
}
}Customizer.withDefaults() は、「Spring Security が用意している標準設定で有効化する」という意味です。
公式ドキュメントでも、Customizer.withDefaults() はデフォルト設定を使ってセキュリティ機能を有効化するショートカットとして説明されています。
変更点3:authorizeRequestsではなくauthorizeHttpRequestsへ寄せる
古いプロジェクトでは、以下のような設定が残っていることがあります。
http
.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated();Spring Security 6 以降では、基本的に authorizeHttpRequests を使う形へ寄せます。
http
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
);公式ドキュメントでも、HttpSecurity を使う場合は少なくとも authorizeHttpRequests を設定する例が示されています。
変更点4:antMatchers / mvcMatchers前提のコードを見直す
Spring Security 7 では、リクエストマッチャー周りも見直しポイントです。
特に以下のような古い書き方が残っている場合は注意です。
http
.authorizeRequests()
.antMatchers("/api/public/**").permitAll()
.mvcMatchers("/admin/**").hasRole("ADMIN");Spring Security 7 では、AntPathRequestMatcher と MvcRequestMatcher はサポートされなくなり、Java DSL では URI を絶対パス、ただし context path を除いた形式で指定する必要があるとされています。また、Spring Security 7 では PathPatternRequestMatcher がデフォルトで使われる方針です。
基本的には、以下のように requestMatchers を使う形に寄せます。
http
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/api/public/**").permitAll()
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
);ここで重要なのは、/api/public/** のように 先頭のスラッシュ付きのパスで書くことです。
変更点5:複数のSecurityFilterChainは「順序」が重要
API と画面でセキュリティ設定を分けたい場合、複数の SecurityFilterChain を定義することがあります。
例えば、以下のようなケースです。
- /api/** : JWT認証
- その他 : フォームログイン
この場合は @Order と securityMatcher を使って、どのリクエストにどの SecurityFilterChain を適用するかを明確にします。
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
@Order(1)
SecurityFilterChain apiSecurityFilterChain(HttpSecurity http) throws Exception {
http
.securityMatcher("/api/**")
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/api/public/**").permitAll()
.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(Customizer.withDefaults())
)
.csrf(csrf -> csrf.disable());
return http.build();
}
@Bean
@Order(2)
SecurityFilterChain webSecurityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/", "/login", "/css/**", "/js/**").permitAll()
.anyRequest().authenticated()
)
.formLogin(form -> form
.loginPage("/login")
.permitAll()
);
return http.build();
}
}FilterChainProxy は複数の SecurityFilterChain がある場合、最初にマッチしたチェーンだけを適用します。公式ドキュメントでも、/api/** にマッチした場合は、そのチェーンだけが呼び出され、他のチェーンは呼び出されないと説明されています。
そのため、より具体的な /api/** のようなチェーンを先に定義し、汎用的なチェーンを後ろに置くのが基本です。
変更点6:Custom DSLはapply()ではなくwith()へ
独自のセキュリティ DSL を使っているプロジェクトでは、以下のようなコードがあるかもしれません。
http
.apply(new MyCustomDsl())
.and()
.authorizeHttpRequests(authorize -> authorize
.anyRequest().authenticated()
);Spring Security 7 では .and() を前提にした設定が使えなくなるため、apply() ではなく with() を使う形へ移行します。公式ドキュメントでも、Spring Security 6.2 から apply() は非推奨となり、7.0 では削除されるため、代わりに .with() を使うことが推奨されています。
http
.with(new MyCustomDsl(), customizer -> {
customizer.enabled(true);
})
.authorizeHttpRequests(authorize -> authorize
.anyRequest().authenticated()
);独自 DSL を使っていないプロジェクトでは、この対応は不要です。
変更点7:dispatcherTypeMatchersを使う場面がある
エラーページや forward されたリクエストをどう扱うかを制御している場合、shouldFilterAllDispatcherTypes(false) のような古い設定が残っていることがあります。
Spring Security 7 へ向けては、dispatcherTypeMatchers を使う形に寄せます。
import jakarta.servlet.DispatcherType;
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize -> authorize
.dispatcherTypeMatchers(DispatcherType.ERROR).permitAll()
.requestMatchers("/error").permitAll()
.anyRequest().authenticated()
);
return http.build();
}エラーページが 403 になってしまう、例外ハンドリング後の画面遷移がうまくいかない、といった場合はこのあたりを確認するとよいです。
APIサーバー向けの実装例
画面を持たない REST API / BFF では、以下のような設定がよく使われます。
import static org.springframework.security.config.Customizer.withDefaults;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.cors(withDefaults())
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/actuator/health").permitAll()
.requestMatchers("/api/public/**").permitAll()
.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(withDefaults())
);
return http.build();
}
}ポイントは以下です。
- APIではCSRFを無効化することが多い
- セッションレスならSessionCreationPolicy.STATELESSを指定する
- 公開APIやhealth checkは明示的にpermitAllする
- JWTを使う場合はoauth2ResourceServer().jwt()をLambda DSLで書く
ただし、CSRFを無効化してよいかはアプリの認証方式によります。
Cookieベースのログインやブラウザ画面がある場合は、単純に無効化せず、要件に応じて検討してください。
画面ありWebアプリ向けの実装例
フォームログインを使う Web アプリでは、以下のようになります。
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/", "/login", "/css/**", "/js/**", "/images/**").permitAll()
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.formLogin(form -> form
.loginPage("/login")
.defaultSuccessUrl("/", true)
.permitAll()
)
.logout(logout -> logout
.logoutUrl("/logout")
.logoutSuccessUrl("/login?logout")
.permitAll()
);
return http.build();
}
}画面ありアプリの場合は、以下を忘れがちです。
- ログイン画面をpermitAllする
- CSS / JS / 画像などの静的リソースをpermitAllする
- 管理画面などの権限設定を明示する
- ログアウト後の遷移先を決める
まとめ
Spring Security 7 への移行では、SecurityFilterChain がなくなるわけではありません。
むしろ、SecurityFilterChain を中心にした設定はそのまま使い続けます。
一方で、設定の書き方は Lambda DSL 前提になります。
特に重要なのは以下です。
- SecurityFilterChain Bean は継続して使う
- Lambda DSL に統一する
- .and() を使わない
- authorizeHttpRequests を使う
- requestMatchers へ寄せる
- 複数チェーンでは @Order と securityMatcher を明示する
移行作業では、一気に全部直そうとするとつらくなります。
まずは Spring Security 6.5 系で非推奨コードを減らし、設定を Lambda DSL に寄せてから 7 へ上げるのが安全です。
Spring Security の設定は少し読みにくく感じることもありますが、SecurityFilterChain の単位で「どのリクエストに、どの認証・認可ルールを適用するのか」を整理すると、移行作業はかなり進めやすくなります。
是非フォローしてください
最新の情報をお伝えします
