【Springセキュリティ】セキュリティ設定クラス(SecurityFilterChain)の書き方
新人
「先輩、Spring Securityのセキュリティ設定って難しそうに見えるんですが、どうやって始めればいいんでしょうか?」
先輩
「まずはSecurityFilterChainというクラスを使って、アクセス制御のルールを定義するところから始めるのが基本だよ。」
新人
「SecurityFilterChainって聞いたことあるけど、どんな役割があるんですか?」
先輩
「それじゃあ、Spring Security セキュリティ設定クラスの仕組みと一緒に、SecurityFilterChainとは何かを順番に見ていこうか。」
1. セキュリティ設定クラスとは?(なぜ必要なのか)
Springアプリケーションにおいて、セキュリティ設定クラスは、Webアクセスに関する「誰が・どのページに・どのようにアクセスできるか」を制御する重要な役割を持っています。
Spring Securityは非常に柔軟で強力なセキュリティフレームワークですが、その分設定も細かくなりがちです。その設定を一元的に管理できるのが、セキュリティ設定クラスです。
以前のSpring Securityでは、WebSecurityConfigurerAdapterを継承して設定を記述していましたが、現在は非推奨となり、代わりにSecurityFilterChainを@Beanで登録する方法が主流です。
このSecurityFilterChainを使って、以下のような設定を行います:
- 認証(ログイン)のルール
- 認可(アクセス制御)のルール
- CSRFやセッション管理などのセキュリティポリシー
Spring Security セキュリティ設定クラスを用意することで、アプリケーション全体のセキュリティ方針を明確に定義でき、安全で保守性の高いWebアプリを構築することができます。
2. SecurityFilterChainとは何か(基本概念と役割)
SecurityFilterChainとは、Spring Securityが提供する「フィルタの並び(チェーン)」のことです。ユーザーからのリクエストがサーバーに届く前に、このチェーンが実行されて、適切なセキュリティチェックが行われます。
リクエストが届いたとき、SpringはこのSecurityFilterChainに沿って、ログイン状態の確認や、アクセス権のチェックなどを自動で行います。
具体的には、次のような設定を記述してフィルタチェーンを構築します。
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authz -> authz
.anyRequest().authenticated() // すべてのリクエストはログイン後に許可
)
.formLogin(); // デフォルトのログインフォームを使用
return http.build();
}
}
このように、SecurityFilterChainの中でauthorizeHttpRequestsやformLoginなどの設定をチェーンメソッドでつなぐことで、柔軟にセキュリティポリシーを定義できます。
このコードでは、すべてのリクエストに対してログインが必要となっており、認証されていないユーザーはログインページにリダイレクトされるようになっています。
Spring Security セキュリティ設定クラスの中心となるこのSecurityFilterChainを理解することで、セキュアなアプリケーション開発の第一歩を踏み出すことができます。
3. SecurityFilterChainの基本的な記述パターン(構文)
SecurityFilterChain 書き方を理解するためには、構成要素を分解して考えることが大切です。設定クラスでは、HttpSecurityを使って、どのURLにどういうセキュリティルールを適用するかを順に指定していきます。
まずは最もシンプルなパターンを見てみましょう。
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authz -> authz
.anyRequest().authenticated() // 全リクエストに認証を要求
)
.formLogin(); // デフォルトのログイン画面を使用
return http.build();
}
}
この基本構文の流れは次のとおりです。
- authorizeHttpRequests():リクエストごとのアクセス制御を設定
- formLogin():ログインフォームの設定(省略時はデフォルト画面)
- build():設定を適用してセキュリティチェーンを構築
このように、メソッドチェーン形式で読みやすく設計されているのがSecurityFilterChainの特徴です。
4. 認証の設定(ログイン画面・ログイン成功後の遷移先)
Spring Securityでは、認証機能としてformLogin()を使ってログイン関連の設定を細かく制御できます。
下記はログインページのURL、ログイン成功後の遷移先などを指定した例です。
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authz -> authz
.anyRequest().authenticated()
)
.formLogin(form -> form
.loginPage("/login") // 独自ログインページのパス
.defaultSuccessUrl("/home") // ログイン成功時の遷移先
.permitAll() // ログインページへのアクセスは誰でも許可
);
return http.build();
}
}
このようにformLogin()メソッドにラムダ式で設定を追加することで、ログインの挙動を細かく制御できます。
loginPage():ログインフォームとして使用するURLを指定defaultSuccessUrl():ログイン成功後の遷移先permitAll():ログインページへのアクセスは全ユーザーに許可
注意点として、loginPage()で指定したURLに対応する@ControllerとHTML画面が存在しない場合、アプリケーションが起動できなくなるので注意してください。
@Controller
public class LoginController {
@GetMapping("/login")
public String loginForm() {
return "login"; // templates/login.html を表示
}
}
これで、Spring Securityにおけるログイン画面のカスタマイズが可能になります。
5. 認可の設定(URLごとのアクセス制御)
Spring Security 認可設定では、ユーザーがアクセスできるページをURL単位で制御することができます。
以下のコードは、URLごとにアクセスを制限する代表的なパターンです。
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authz -> authz
.requestMatchers("/admin/**").hasRole("ADMIN") // /admin配下はADMIN権限のみ
.requestMatchers("/user/**").hasAnyRole("USER", "ADMIN") // /user配下はUSERまたはADMIN
.requestMatchers("/", "/login", "/about").permitAll() // 誰でもアクセス可
.anyRequest().authenticated() // その他は認証必須
)
.formLogin(form -> form
.loginPage("/login")
.defaultSuccessUrl("/home")
.permitAll()
);
return http.build();
}
}
この設定では以下のような制御が行われます:
- /admin/**:ADMINロールを持つユーザーのみアクセス可能
- /user/**:USERまたはADMINロールを持つユーザーがアクセス可能
- /, /login, /about:誰でもアクセス可能(未ログインでもOK)
- それ以外のリクエスト:ログイン済みであることが必要
このようにrequestMatchers()とhasRole()やpermitAll()などを組み合わせることで、柔軟な認可設定が可能です。
なお、hasRole("ADMIN")は内部的にROLE_ADMINという権限とマッチします。権限の登録時には必ずROLE_を接頭辞として付けるようにしましょう。
認可のルールを明確に分けることで、機能別・権限別にページへのアクセス制御が可能となり、アプリケーションの安全性を高めることができます。
6. CSRF対策の設定(有効化と無効化の方法)
Spring Securityでは、デフォルトでCSRF対策が有効になっています。CSRF(クロスサイトリクエストフォージェリ)とは、ログイン中のユーザーのセッションを悪用し、意図しないリクエストを送信させる攻撃です。
通常のフォーム送信ではCSRFトークンが必要になるため、テンプレートエンジン(Thymeleafなど)を使ってトークンを埋め込む必要があります。しかし、開発初期やAPI開発ではこのチェックが邪魔になる場合もあります。
ここでは、CSRF設定を無効にする方法を紹介します。
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable()) // CSRF保護を無効化
.authorizeHttpRequests(authz -> authz
.anyRequest().authenticated()
)
.formLogin();
return http.build();
}
}
上記のように、csrf().disable()を追加することでCSRFチェックを無効化できます。
注意: 本番環境ではCSRF対策は必ず有効にするようにしてください。無効化は開発中の一時的な対応にとどめておくべきです。
テンプレートエンジンでのトークン埋め込みや、CSRFトークンの取得方法については別途学習が必要になります。
7. ログアウトの設定(ログアウトURLと処理)
Spring Securityでは、ログアウト処理もSecurityFilterChainの中で簡単に定義できます。
ログアウトに関する基本設定の例は以下のとおりです。
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authz -> authz
.anyRequest().authenticated()
)
.formLogin()
.and()
.logout(logout -> logout
.logoutUrl("/logout") // ログアウト用URL
.logoutSuccessUrl("/login?logout") // ログアウト後の遷移先
.invalidateHttpSession(true) // セッション破棄
.deleteCookies("JSESSIONID") // Cookie削除
);
return http.build();
}
}
この設定により、/logoutへPOSTリクエストを送ることで、ログアウト処理が実行されます。
補足: GETメソッドでのログアウトは推奨されておらず、POSTで行う必要があります。HTML側では以下のようにフォームを使用してください。
<form action="/logout" method="post">
<input type="submit" value="ログアウト">
</form>
セッションの無効化やCookieの削除まで自動で行ってくれるため、ログアウト時のセキュリティ対策も万全です。
8. よくある設定ミスとその解決方法
SecurityFilterChain トラブルシューティングとして、初心者がよくつまずくポイントとその解決方法を紹介します。
① フィルタが動いていない
セキュリティ設定を書いたのに、ログイン画面が表示されなかったり、全ページにアクセスできてしまうといった問題が起きることがあります。
原因: セキュリティ設定クラスに@Configurationや@EnableWebSecurityの記述が抜けている場合。
解決策: 下記のように必ずアノテーションを付けること。
@Configuration
@EnableWebSecurity
public class SecurityConfig {
// 設定クラスの中身
}
② 静的リソースにアクセスできない
CSSや画像ファイルが読み込まれない場合があります。
原因: セキュリティ設定で/css/**などの静的ファイルのパスに許可設定をしていない。
解決策: requestMatchersを使って明示的に許可する。
http
.authorizeHttpRequests(authz -> authz
.requestMatchers("/css/**", "/js/**", "/images/**").permitAll()
.anyRequest().authenticated()
)
③ ルートURL(/)にアクセスできない
原因: requestMatchers(\"/\")の記述がなく、anyRequest().authenticated()によってブロックされている。
解決策: ホーム画面などはpermitAll()で明示的に許可する。
http
.authorizeHttpRequests(authz -> authz
.requestMatchers("/").permitAll()
.anyRequest().authenticated()
)
このように、細かな設定ミスでも意図した挙動にならないことがあります。SecurityFilterChainの構成を丁寧に見直すことで、問題の原因を早期に発見できるようになります。