最初のSpring Security - フォーム認証＆画面遷移

## はじめに

Spring Security シリーズ第1回目です。

- 第1回：最初のSpring Security - フォーム認証＆画面遷移（本記事）
- 第2回：[Spring Securityのログインページをカスタマイズする](https://qiita.com/huge-book-storage/items/3dbf92dac5ba7c812893)
- 第3回：[ログイン認証のDBアクセス処理を実装](https://qiita.com/huge-book-storage/items/69485cd8a7b3bc589d4a)

本記事ではSpring Securityに初めて触れることを想定して、できるだけ少ないコード量で、一般的な認証を挟むページ遷移を実装することを目指します。

Spring Securityは難しく扱いづらい印象があるため、ひとつひとつ確認するように機能を見ていきたいと思います。

## 実装すること、確かめること

本記事では次の事項を実装して動作確認することを目的とします。

- ログイン認証が求められないページは、ログインしなくても閲覧できること
- ログインページにてフォーム認証が行えること
- ログイン認証後、成功ページに遷移すること
- 未認証時にログイン認証を求められてログインページに飛ばされた場合、ログイン後に元のページに戻って来られること

## FW、ミドルウェア
本記事では下記バージョンを使用します。

Spring Boot 2.1.5
Spring Security 5.1.5（maven依存）
Java 11（AdoptOpenJDK）

## 実装

### プロジェクト作成

[Spring Initializr](https://start.spring.io/)から以下の依存を追加して、プロジェクトをダウンロードします。

- Web
- Security

maven依存としては次のものが追加されます。

```xml:pom.xml
<!-- 中略 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- 中略 -->

```

### Spring Security設定
次のファイルを追加してSpring Securityの設定を行います。


```DemoWebSecurityConfig.java
@Configuration
class DemoWebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            // AUTHORIZE
            .authorizeRequests()
            /* */.mvcMatchers("/hello").permitAll()
            /* */.anyRequest()
            /*    */.authenticated()
            .and()
            // LOGIN
            .formLogin()
            /* */.defaultSuccessUrl("/success")
        // end
        ;
    }
}
```

- `authorizeRequests()`で対象リクエストを指定します。
  - `mvcMatchers("/hello").permitAll()`で、helloページ（パスが「/hello」）は認証外（常にアクセス許可）と指定します。
  - `anyRequest().authenticated()`にて、すべてのリクエスト（ただし上で認証外に指定した「/hello」は除く）を要認証とします。
- `formLogin()`でフォーム認証の設定を行います。
  - `defaultSuccessUrl("/success")`でログイン成功後のデフォルトページを指定します

### 遷移確認用ページの用意

ページ遷移の確認のため、必要最小限のコントローラを用意しておきます。

``` DemoController.java
@RestController
class DemoController {
    @GetMapping(path = "hello", produces = "text/html")
    public String hello() {
        return "Hello!!!";
    }

    @GetMapping(path = "success", produces = "text/html")
    public String success() {
        return "成功!!!";
    }

    @GetMapping(path = "page1", produces = "text/html")
    public String page1() {
        return "ページ１";
    }
}

```

## 動作確認

### helloページ
helloページは認証外としているため、未ログイン状態でも閲覧することができます。

```html:/hello
Hello!!!
```

### ログインページ表示 
/login でログインページを開きます。
これはSpring Securityのデフォルトログインページです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/299510/f8c86f05-f351-cc92-dafa-f79eea585575.png)


### ID、パスワード入力誤り
ID、パスワードを誤るとログインページに戻ってきてエラー表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/299510/9e351fab-11f2-84b8-8af9-359b8c9683e8.png)

### ログイン成功
何も設定がない場合、ID / PASSは次のようになります。

- ID: 「user」
- PASS: コンソール出力されるもの（次のようなデータが出力されます）

```
Using generated security password: 62171779-ae34-53a2-c926-34f96e922db6
```

ログイン後のデフォルトページに遷移します。

```html:/success
成功!!!
```

### ログアウト
/logout でログアウトページに遷移します。
これはSpring Securityのデフォルトログアウトページです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/299510/f2830848-de9e-19a0-20d9-cb9e0a5b65d3.png)

ボタンを押すとログアウト実行してログインページに戻ります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/299510/044655a1-5be4-a1f5-4326-e6ea1259ea2e.png)


### 要認証ページを開いてログインページにリダイレクト
ログアウト後、認証が求められるページ /page1 にアクセスすると、ログインページにリダイレクトされます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/299510/f8c86f05-f351-cc92-dafa-f79eea585575.png)

ログインに成功すると、元々アクセスしたページに遷移します。

```html:/page1
ページ１
```

## おわりに

「最初のSpring Security」ということで、できるだけ少ないコードでどこまで実現できるか試してみましたが、たったこれだけのコード量で基本的な認証フローが実装できるのは結構すごいのではと思います。

中々とっつきづらいSpring Securityですが、こうやって徐々に理解を深めていければと思っています。

## 参考記事
[Spring Security 使い方メモ　認証・認可](https://qiita.com/opengl-8080/items/032ed0fa27a239bdc1cc)