この記事について
最近(5.x)のSpring Securityでは、セキュリティ設定の書き方が大幅に変わりました。その背景と、新しい書き方を紹介します。
非推奨になったものは、将来的には削除される可能性もあるため、なるべく早く新しい書き方に移行することをおすすめします。
この記事は、Spring Securityのアーキテクチャの理解(Filter Chain、 AuthenticationManager
、 AccessDecisionManager
など)を前提としています。あまり詳しくない方は、まずopengl_8080さんのブログを読むことをおすすめします。
サンプルコード -> https://github.com/MasatoshiTada/spring-security-intro
忙しい人のためのまとめ
-
WebSecurityConfigurerAdapter
を継承せずにSecurityFilterChain
をBean定義してセキュリティ設定を書きましょう -
http.authorizeRequests()
ではなくhttp.authorizeHttpRequests()
を使いましょう -
web.ignoring()
ではなくhttp.authorizeHttpRequests().mvcMatchers().permitAll()
を使いましょう
具体的なコード例はこちら
環境
- JDK 17
- Spring Security 5.7.1
- Spring Boot 2.7.0
将来のバージョン変更によって、この記事の内容が正しくなくなる可能性もあります。
重要な変更点たち
[Spring Security 5.4] SecurityFilterChain
をBean定義できるようになった
対応するIssue → https://github.com/spring-projects/spring-security/issues/8804
SecurityFilterChain
自体はかなり以前からあったものです。これをBean定義できるようになったことにより、 従来のように WebSecurityConfigurerAdapter
を継承しなくても、設定が記述できるようになりました。
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin(login -> login
...
}
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.formLogin(login -> login
...
[Spring Security 5.4] WebSecurityCustomizer
が導入された
対応するIssue -> https://github.com/spring-projects/spring-security/issues/8978
静的リソースなどをSpring Securityによる保護の対象外にしたい場合、従来は WebSecurityConfigurerAdapter
を継承して configure(webSecurity)
をオーバーライドしていました。
同様の設定を、 WebSecurityCustomizer
をBean定義することで記述できるようになりました。
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(webSecurity web) throws Exception {
web.ignoring().mvcMatchers("/css/**");
}
@EnableWebSecurity
public class SecurityConfig {
@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return web -> web.ignoring().mvcMatchers("/css/**");
}
ただし上記の設定は、指定したURLに対してはSpring Securityが完全に何もしなくなるため、安全でなくなる可能性があります。実際、この設定を記述していると起動時に次のようなWARNログが出力されます。
...
20xx-xx-xx xx:xx:xx.xxx WARN 3029 --- [ main] o.s.s.c.a.web.builders.WebSecurity : You are asking Spring Security to ignore Mvc [pattern='/css/**']. This is not recommended -- please use permitAll via HttpSecurity#authorizeHttpRequests instead.
...
同様の設定については、 permitAll()
を使った書き方が推奨されています(Javadoc参照)。
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(authz -> authz
.mvcMatchers("/css/**").permitAll()
// Spring Boot利用時は下記でも同様
// .requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
...
セキュリティ無視の設定以外で WebSecurity
を使うことはあまり無いと思いますので、 WebSecurityCustomizer
を使う機会もあまり無いかもしれません。
[Spring Security 5.5] AuthorizationManager
が導入された
対応するIssue -> https://github.com/spring-projects/spring-security/issues/8900
従来、Spring Securityの認可処理は AccessDecisionManager
が行っています。これを置き換えるクラスが AuthorizationManager
です(👆のIssueによれば、 AccessDecisionManager
は将来的には非推奨になるようです)。
また、従来は FilterSecurityInterceptor
というフィルターが AccessDecisionManager
を使って認可処理をしています。このフィルターを置き換えるべく、 AuthorizationManager
を使った AuthorizationFilter
も導入されました。
AuthorizationFilter
を使うには、 http.authorizeHttpRequests()
を利用して設定を記述します( http.authorizeRequests()
を利用した場合、従来どおり FilterSecurityInterceptor
が使われます)。
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeRequests(authz -> authz
.mvcMatchers("/css/**").permitAll()
...
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(authz -> authz
.mvcMatchers("/css/**").permitAll()
...
[Spring Security 5.7] WebSecurityConfigurerAdapter
が非推奨になった
対応するIssue -> https://github.com/spring-projects/spring-security/issues/10822
ここまでで紹介した変更により WebSecurityConfigurerAdapter
が不要になったため、非推奨になりました。
なぜこれらの変更が行われたのか?
Issue(これとかこれ)を読む限りでは、WebFlux版のSpring Securityと書き方を揃えたいという意向が読み取れます。
もう1点は完全に僕の想像ですが、既存のあまり良くないAPIを変えたかったのではないでしょうか。例えば WebSecurityConfigurerAdapter
には、引数の型が違う configure()
メソッドが3つもあります。また AccessDecisionManager
による認可は、 AccessDecisionManager
から更に複数の AccessDecisionVoter
に委譲してそれらの結果をまとめて判断する、という複雑な処理になっています。
新しい書き方のコード例
ここまでをまとめて、実際のコード例はこのようになります(全体像はGitHubに置いてあります)。
package com.example.springsecurityintro;
import org.springframework.boot.autoconfigure.security.servlet.PathRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.formLogin(login -> login
.loginProcessingUrl("/login")
.loginPage("/login")
.defaultSuccessUrl("/")
.failureUrl("/login?error")
.permitAll()
).logout(logout -> logout
.logoutSuccessUrl("/")
).authorizeHttpRequests(authz -> authz
.requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
.mvcMatchers("/").permitAll()
.mvcMatchers("/general").hasRole("GENERAL")
.mvcMatchers("/admin").hasRole("ADMIN")
.anyRequest().authenticated()
);
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
合わせて読みたい
Spring公式ブログにより詳細な解説があります。