LoginSignup
274
222

Spring Security 5.4〜6.0でセキュリティ設定の書き方が大幅に変わる件

Last updated at Posted at 2022-05-30

この記事について

最近(5.4〜6.0)のSpring Securityでは、セキュリティ設定の書き方が大幅に変わりました。その背景と、新しい書き方を紹介します。

非推奨になったものは、将来的には削除される可能性もあるため、なるべく早く新しい書き方に移行することをおすすめします。(既に削除されたものもあります)

この記事は、Spring Securityのアーキテクチャの理解(Filter Chain、 AuthenticationManagerAccessDecisionManager など)を前提としています。あまり詳しくない方は、まずopengl_8080さんのブログを読むことをおすすめします。

サンプルコード -> https://github.com/MasatoshiTada/spring-security-intro

忙しい人のためのまとめ

  1. @Configuration を必ず付加しましょう
  2. WebSecurityConfigurerAdapter を継承せずに SecurityFilterChain をBean定義してセキュリティ設定を書きましょう
  3. http.authorizeRequests() ではなく http.authorizeHttpRequests() を使いましょう
  4. @EnableGlobalMethodSecurity ではなく @EnableMethodSecurity を使いましょう
  5. antMatchers()mvcMatchers() ではなく requestMatchers() を使いましょう
  6. web.ignoring() ではなく permitAll() を使いましょう
  7. .and() は使わずにラムダ式で書きましょう

具体的なコード例はこちら

環境

  • JDK 17
  • Spring Security 6.0.1
  • Spring Boot 3.0.2

将来のバージョン変更によって、この記事の内容が正しくなくなる可能性もあります。

重要な変更点たち

[Spring Security 5.4] SecurityFilterChain をBean定義できるようになった

対応するIssue → https://github.com/spring-projects/spring-security/issues/8804

SecurityFilterChain 自体はかなり以前からあったものです。これをBean定義できるようになったことにより、 従来のように WebSecurityConfigurerAdapter を継承しなくても、設定が記述できるようになりました。

5.3以前
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin(login -> login
                ...
    }
5.4以降
@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定義することで記述できるようになりました。

5.3以前
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().mvcMatchers("/css/**");
    }
5.4以降(ただし推奨されない)
@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
                .requestMatchers("/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
                .requestMatchers("/css/**").permitAll()
                ...

[Spring Security 5.6] @EnableMethodSecurity が導入された

対応するIssue -> https://github.com/spring-projects/spring-security/issues/9289

AOPによるメソッドの認可処理では、従来は @EnableGlobalMethodSecurity が使われていました。これは AccessDesicionManager  を使ったものです。

そこで、 AuthorizationManager を使う @EnableMethodSecurity が導入されました。

[Spring Security 5.7+6.0] WebSecurityConfigurerAdapter が非推奨→削除になった

対応するIssue -> https://github.com/spring-projects/spring-security/issues/10822https://github.com/spring-projects/spring-security/issues/10902

ここまでで紹介した変更により WebSecurityConfigurerAdapter が不要になったため、5.7で非推奨化、6.0で削除になりました。

[Spring Security 5.8+6.0] antMatchers()mvcMatchers() が非推奨→削除になった

対応するIssue -> TBD

antMatchers()mvcMatchers() が5.8で非推奨化、6.0で削除されました。

代わりに requestMatchers() を使います。

5.8と6.0は同時にリリースされています。控えめに言って鬼ですね・・・

[Spring Security 6.0] @EnableWebSecurity から @Configuration が削除された

対応するIssue -> https://github.com/spring-projects/spring-security/issues/6613

Spring Security 5.8までは @EnableWebSecurity@Configuration が付加されていたため、 @EnableWebSecurity を付加するだけでJava Configクラスと認識されていました。

しかし6.0で @EnableWebSecurity から @Configuration が削除されたため、Java Configクラスには明示的に @Configuration を付加する必要があります。

非推奨とか削除じゃないので気づきづらい、一番イヤな変更点ですw

[Spring Security 7.0] .and()が無効になる

対応するIssue -> TBD

Spring Security Referenceに、Spring Security 7.0への移行に関する説明が追加されました。

これによると、従来使われてきた .and() を使った設定記述が無効(おそらくですが削除されるものと思われます)になるとのことです。

and()を利用した書き方
        http.formLogin()
                .loginProcessingUrl("/login")
                .loginPage("/login")
                .defaultSuccessUrl("/")
                .failureUrl("/login?error")
                .permitAll()
                .and()
        .logout()
                .logoutSuccessUrl("/")
                .and()
        ...

これはIDEなどでインデントを整えると次のようになってしまい、どこからどこまでが1つのかたまりなのかが分かりづらくなってしまいます。

and()を利用した書き方をIDEでフォーマットした結果
        http.formLogin()
            .loginProcessingUrl("/login")
            .loginPage("/login")
            .defaultSuccessUrl("/")
            .failureUrl("/login?error")
            .permitAll()
            .and()
            .logout()
            .logoutSuccessUrl("/")
            .and()
            ...

Spring Security 5.2で導入されたラムダ式による記述を使うと、その心配はありません。

ラムダ式を利用した書き方
        http.formLogin(login -> login
                .loginProcessingUrl("/login")
                .loginPage("/login")
                .defaultSuccessUrl("/")
                .failureUrl("/login?error")
                .permitAll()
        ).logout(logout -> logout
                .logoutSuccessUrl("/")
        )...

Spring Security 7.0のリリース日は、2023年5月現在、まだ決まっていません。しかし、徐々にラムダ式に書き換えていったほうがいいでしょう。

なぜこれらの変更が行われたのか?

Issue(これとかこれ)を読む限りでは、WebFlux版のSpring Securityと書き方を揃えたいという意向が読み取れます。

もう1点は完全に僕の想像ですが、既存のあまり良くないAPIを変えたかったのではないでしょうか。例えば WebSecurityConfigurerAdapter には、引数の型が違う configure() メソッドが3つもあります。また AccessDecisionManager による認可は、 AccessDecisionManager から更に複数の AccessDecisionVoter に委譲してそれらの結果をまとめて判断する、という複雑な処理になっています。

新しい書き方のコード例

ここまでをまとめて、実際のコード例はこのようになります(全体像はGitHubに置いてあります)。

SecurityConfig.java
package com.example.springsecurityintro;

import org.springframework.boot.autoconfigure.security.servlet.PathRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
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;

@Configuration
@EnableWebSecurity
@EnableMethodSecurity
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()
                .requestMatchers("/").permitAll()
                .requestMatchers("/general").hasRole("GENERAL")
                .requestMatchers("/admin").hasRole("ADMIN")
                .anyRequest().authenticated()
        );
        return http.build();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

合わせて読みたい

5.7までの変更点については、Spring公式ブログにより詳細な解説があります。

追加

5.8以降から、ログイン後のURLにcontinueクエリパラメータが付く場合があります。詳細は👇の記事を参照してください。

274
222
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
274
222