超基本的なSpring Securityの解説(WebFlux)

# Spring Securityとは
公式では下記の通り。
```
Spring Security は、認証、認可、一般的な攻撃に対する保護を提供するフレームワークです。

命令型およびリアクティブ両方のアプリケーションに対するファーストクラスのサポートにより、
Springベースのアプリケーションを保護するためのデファクトスタンダードとなっています。
```
https://spring.pleiades.io/spring-security/reference/

ログイン認証や権限による認可、CSRFのような一般的なセキュリティ対策ができるライブラリですよってことですね。
Springだったら大体使えるので、これを使ってセキュリティ対策していこうな！ってことです。

今回は、Spring Securityの中でも、リアクティブアプリケーションについて解説していきます。

# 導入方法
Gradleに以下の依存関係を追加すればOKです。
```gradle
implementation "org.springframework.boot:spring-boot-starter-security"
```
https://spring.pleiades.io/spring-security/reference/getting-spring-security.html

# サンプルコード
```java
    @Bean
    public SecurityWebFilterChain securityFilterChain(ServerHttpSecurity http) {
        http
            .csrf(csrf -> csrf.csrfTokenRepository(new CookieServerCsrfTokenRepository()))
            .authorizeExchange(exchanges -> exchanges
                    .pathMatchers(
                            "/js/**",
                            "/css/**",
                            "/image/**",
                            "/login/**",
                            "/error/**"
                    ).permitAll()
                    .anyExchange().authenticated()
            )
            .formLogin(formLogin -> formLogin
                    .loginPage("/login")
                    // ログイン成功時の遷移先を設定
                    .authenticationSuccessHandler(new RedirectServerAuthenticationSuccessHandler("/top"))
                    // ログイン失敗時の遷移先を設定
                    .authenticationFailureHandler(new RedirectServerAuthenticationFailureHandler("/error"))
            );
        return http.build();
    }
```
## [csrf()](https://spring.pleiades.io/spring-security/site/docs/current/api/org/springframework/security/config/web/server/ServerHttpSecurity.CsrfSpec.html)
CSRFからアプリケーションを保護します。
無効にするにはdisableを指定します。
```java
http.csrf(ServerHttpSecurity.CsrfSpec::disable)
```

## [authorizeExchange()](https://spring.pleiades.io/spring-security/site/docs/current/api/org/springframework/security/config/web/server/ServerHttpSecurity.AuthorizeExchangeSpec.html)
ページやresourceに対してアクセス権限を付与することができます。

#### pathMatchers()
一致するパスにして[後続](#permitAll())の関数で権限を付与することができます。

#### [permitAll()](https://spring.pleiades.io/spring-security/site/docs/current/api/org/springframework/security/config/web/server/ServerHttpSecurity.AuthorizeExchangeSpec.Access.html)
pathMatchers()で指定したパスに対してアクセス権限を付与することができます。
他にも拒否やIPアドレス単位でのアクセスの設定などできます。

#### anyExchange().authenticated()
pathMatchers()で指定したパス以外はログイン認証が必要という設定です。

## [formLogin()](https://spring.pleiades.io/spring-security/site/docs/current/api/org/springframework/security/config/web/server/ServerHttpSecurity.FormLoginSpec.html)
ログイン認証について設定ができます。
認証をするにはユーザの特定が必要ですが、Spring Securityではそのための機構が用意されています。
Spring Security内でfindByUsername()を叩き、その戻り値を利用してユーザの有無の確認やパスワード認証を行います。

ちなみに、Spring Securityのログイン認証はusernameとpasswordの2つのパラメータしか受け付けていないようです。
```HTML
<form action="/login" method="post">
    <h1>ログイン画面</h1>

    <input type="email" name="username">
    <input type="password" name="password">

    <button type="submit">ログイン</button>
</form>
```
```java
@Data
public class SampleUserDetail implements UserDetails {

    private long userId;
    private String userName;
    private String password;

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return Collections.singleton(new SimpleGrantedAuthority("admin"));
    }

    @Override
    public String getPassword() {
        return this.password;
    }

    @Override
    public String getUsername() {
        return this.userName;
    }
}
```

```java
@Service
@RequiredArgsConstructor
public class SampleUserDetailServices implements ReactiveUserDetailsService {
    private final UserRepository userRepository;

    @Override
    public Mono<UserDetails> findByUsername(String username) {

        User user = userRepository.fetchUser(username);

        AdminUserDetails adminUserDetails = new AdminUserDetails();
        adminUserDetails.setUserId(user.getUserId());
        adminUserDetails.setUserName(user.getUserName());
        adminUserDetails.setPassword(user.getPassword());

        return adminUserDetails;
    }
}
```

# まとめ
この形そのままではないかもしれませんが、こんな感じだという感覚が伝われば良きです。
そして、大体はリファレンスに書いてあるので目を通してみてください！

https://spring.pleiades.io/spring-security/site/docs/current/api/org/springframework/security/config/web/server/ServerHttpSecurity.html#csrf(org.springframework.security.config.Customizer)