Spring Boot でSpring Securityを使ってみる（SecurityFilterChain）

## はじめに　
　個人学習で作成中のwebサービスにてログイン機能を作成しようと思い、以前学習していたSpring Securityを使おうとしたのですが、WebSecurityConfigurerAdapterが非推奨となっていたため、別のやり方を調べました。

　今回はそのアウトプットも兼ねて、投稿します。

## 非推奨になった書き方（WebSecurityConfigurerAdapter）
```java

@Configuration
@EnablebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

  @Override 
  public void configure(HttpSecurity http)throws Exception {

   // do something
  }
}

```
最初にも記述した通り、継承しているWebSecurityConfigurerAdapterが非推奨になってしまい、他のやり方もわからずとても困りました...

Spring bootの参考書にもこのやり方しか載ってないことが多いような気がします。
（このやり方以外の方法と詳細が載っている参考書があるならば教えていただきたいです。）

## 結論

■`「　SecurityFilterChain　」`　を使いましょうという事みたいです。

### 具体例
基本的な設定は以下の書き方になります。

```java
package com.example.config;

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.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;


@Configuration
public class SecurityConfig {

	@Bean
	public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
		http.formLogin(login -> login
                // 指定したURLがリクエストされるとログイン認証を行う。
				.loginProcessingUrl("/login") 

                // ログイン時のURLの指定
				.loginPage("/login")  

               // 認証成功後にリダイレクトする場所の指定
				.defaultSuccessUrl("/")  

               // ログインに失敗した時のURL
				.failureUrl("/login?error")  

              //アクセス権限の有無（permitAllは全てのユーザーがアクセス可能)
				.permitAll()                  
		).logout(logout -> logout
				.logoutSuccessUrl("/")

             //アクセス制限
		).authorizeHttpRequests(ahr -> ahr
				.requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
				.mvcMatchers("/").permitAll()

                //"/admin"はADMIN権限のあるものだけがアクセスできる
				.mvcMatchers("/admin").hasRole("ADMIN")  

                //他のリンクは全て認証が必要である。
				.anyRequest().authenticated()           
				);
		/**
		 * HttpSecurityオブジェクトを生成し、オブジェクトもしくはnullを返す。
		 * build()メソッドはHttpSecurityBuilderインターフェースのメソッド。
		 * HttpSecurityBuilderは、
		 * HttpSecurity、WebSecurityなどの実装クラス
		 * HttpSecurityは、DefaultSecurityFilterChainインターフェースを実装しており、
		 * DefaultSecurityFilterChainはSecurityFilterChainを実装しているため、ポリモーフィズム的にも正しい。
		 */
		return http.build();  
		
	}
}

```

以前との違いとしては、

`①WebSecurityConfigurerAdapterを継承する必要がなくなった。`

`②configureメソッドをオーバーライドする必要がなくなった。`

`③メソッドがラムダを使用しているため、少し処理がわかりづらい。（※:ラムダDSLと呼ばれる）`

`④SecurityFilterChainをBean登録し、戻り値として返す必要がある。
（http.build()がHttpSecurityオブジェクトを生成し、戻り値として返す）`

というような感じです。
このSecurityFilterChainについては新しくできたものではなく、ずっと前からあったみたいなのですが、SecurityFilterChainがBean登録できなかったみたいで、Spring Security 5.4からBeanができるようになったみたいです。

詳しくは公式ブログをご確認ください。

**■公式ブログ↓**

https://spring.io/blog/2022/02/21/spring-security-without-the-websecurityconfigureradapter

## 補足（ラムダDSL）について
### ラムダDSLとは、
簡単にいうと、

`ラムダを使用してHTTPセキュリティを設定できるDSLの拡張機能`

のことみたいです。
まだ完全には理解できていないのですが、公式ブログを見た感じ以下のようなメリットがあるようです。

`①and()メソッドで繋ぐ必要がなくなる。`
`②結果として可読性が上がる（コードが読みやすくなる）。`

確かに、
formLogin（ログイン時の認証）、
authorizeRequests（アクセス制御）、
logout（ログアウト時の設定）
といったそれぞれの設定を行うたびにand()メソッドで繋ぐのは書くのもめんどくさいし、メソッドが増えるので読みづらさは少し感じていました。💦


## 具体例
最後に、実際にラムダDSLを使用した場合と使用しなかった場合の例を確認します。
※公式ブログより引用

### ■ラムダDSLを使用する例

```java
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests(authorizeRequests ->
                authorizeRequests
                    .antMatchers("/blog/**").permitAll()
                    .anyRequest().authenticated()
            )
            .formLogin(formLogin ->
                formLogin
                    .loginPage("/login")
                    .permitAll()
            )
            .rememberMe(withDefaults());
    }
}
```


### ■ラムダDSLを使用しない例
```java
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/blog/**").permitAll()
                .anyRequest().authenticated()
                .and() // ←ここ
            .formLogin()
                .loginPage("/login")
                .permitAll()
                .and() // ←ここ
            .rememberMe();
    }
}
```


**公式ブログ（ラムダDSLについて）↓**

https://spring.io/blog/2019/11/21/spring-security-lambda-dsl

##  参考文献
■公式ブログ

https://spring.io/blog/2022/02/21/spring-security-without-the-websecurityconfigureradapter

■Spring securityについてのQiita投稿

https://qiita.com/suke_masa/items/908805dd45df08ba28d8

https://qiita.com/opengl-8080/items/c105152c9ca48509bd0c

https://qiita.com/kazuki43zoo/items/e925f134e65d7595aa3c

■HttpSecurityクラス

https://spring.pleiades.io/spring-security/site/docs/current/api/org/springframework/security/config/annotation/web/builders/HttpSecurity.html

■DefaultSecurityFilterChainクラス

https://spring.pleiades.io/spring-security/site/docs/current/api/org/springframework/security/web/DefaultSecurityFilterChain.html

■SecurityFilterChainインターフェース

https://spring.pleiades.io/spring-security/site/docs/current/api/org/springframework/security/web/SecurityFilterChain.html

■httpSecurityBuilderインターフェース

https://spring.pleiades.io/spring-security/site/docs/current/api/org/springframework/security/config/annotation/web/HttpSecurityBuilder.html

■Spring Securityの設定

https://midorigame-jo.com/spring-security1/