OAuth 2.0 with Spring Security 5.1の認可サーバーとしてKeycloakを使う

# この記事について
[JSUG Spring Fest 2018](http://springfest2018.springframework.jp)で、Spring Security 5.1によるOAuth 2.0の実装例を紹介します。

しかし、Spring Security 5.1にはクライアントとリソースサーバーの機能しかないため、認可サーバーにはKeycloakを使いました。

この記事では、今回のサンプルでの認可サーバーとしてKeycloakを使うためのインストールや設定方法を紹介します。

Keycloakのバージョンは4.5.0.Finalです。

クライアントとリソースサーバーは[GitHub](https://github.com/MasatoshiTada/oauth2-with-spring-security-51)に公開しています。

# 事前準備
JDK 8以上のインストールが必要です。

# ダウンロードとインストール
[公式ページ](https://www.keycloak.org/downloads.html)から、[Standalone server distribution]のZIPファイルをダウンロードします。そして、ローカルの適当なフォルダに展開するだけです。

Keycloak自体はアプリケーションなのですが、このZIPはWildFlyにデプロイされた状態になっていますので、単独で動作します。


# 起動
`Keycloakを展開したフォルダ/bin` の中の `standalone.sh` (Windowsの場合は `standalone.bat` )で起動します。

今回はポート番号9000で起動したいため、 `-Djboss.http.port=9000` オプションを付加しています。

```bash
$ cd bin
$ ./standalone.sh -Djboss.http.port=9000
```


# 管理者名・パスワードの設定
Webブラウザで http://localhost:9000 を開き、画面左側の[Administration Console]に、管理者のユーザー名・パスワードを設定して[Create]をクリックします。

![スクリーンショット 2018-10-21 12.13.57.png](https://qiita-image-store.s3.amazonaws.com/0/58650/a8076f7d-283f-bedc-f391-5a87d046327a.png)

成功すると[User Created]と表示されます。

![スクリーンショット 2018-10-21 12.22.58.png](https://qiita-image-store.s3.amazonaws.com/0/58650/6b2e8e21-ab49-9712-ce09-0c4b509cf20a.png)

[Administration Console]の部分をクリックすると管理者ログイン画面が表示されます。

![スクリーンショット 2018-10-21 12.24.35.png](https://qiita-image-store.s3.amazonaws.com/0/58650/fe795aa2-dd7e-7201-4a1b-6db665758bf4.png)

設定したユーザー名・パスワードを入力して[Log In]をクリックします。


# レルムの作成
画面左上の[Master]のあたりにマウスカーソルを合わせると出てくる[Add realm]をクリックします。

![スクリーンショット 2018-10-21 12.26.39.png](https://qiita-image-store.s3.amazonaws.com/0/58650/7f7ad074-eed1-49cd-b7ef-6f64c7974110.png)

[Name]に任意のレルム名（今回は `todo-api` ）を入力して[Create]をクリックします。

![スクリーンショット 2018-10-21 12.28.57.png](https://qiita-image-store.s3.amazonaws.com/0/58650/940a6b0e-3a47-4453-8ec4-8ffda888464c.png)

[Tokens]タブを選択して、[Access Token Lifespan]を[1 Minutes]に設定します。

> これは、Keycloakが発行するアクセストークンの有効期間を設定しています。デフォルトは5分ですが、説明のため短めにしています。

![スクリーンショット 2018-10-21 12.30.44.png](https://qiita-image-store.s3.amazonaws.com/0/58650/6c7ebdd3-a1f7-9e52-8fe8-b17a5c211dc5.png)

下の方にスクロールして[Save]をクリックします。

![スクリーンショット 2018-10-21 12.34.53.png](https://qiita-image-store.s3.amazonaws.com/0/58650/0cd180d8-de8d-935a-b881-b934fb92e018.png)


# ユーザーの設定
画面左側の[Users]をクリックし、画面右側の[Add user]をクリックします。

![スクリーンショット 2018-10-21 13.02.09.png](https://qiita-image-store.s3.amazonaws.com/0/58650/6f32f3be-2608-7ba4-b770-cf5c2e64d1e3.png)

[Username]に `user` と入力して [Save] をクリックします。

![スクリーンショット 2018-10-21 13.04.39.png](https://qiita-image-store.s3.amazonaws.com/0/58650/e23af614-f383-8e51-2ece-112c318255e1.png)

[Credentials]タブを開き、[New Password]と[Password Confirmation]にパスワードを設定し、[Temporary]を[OFF]にして[Reset Password]をクリックします。

![スクリーンショット 2018-10-21 13.06.52.png](https://qiita-image-store.s3.amazonaws.com/0/58650/a369f4bd-218b-f1e6-de52-409407a3d7e4.png)

ダイアログが出てきたら[Change Password]を選択してください。

![スクリーンショット 2018-10-21 13.07.47.png](https://qiita-image-store.s3.amazonaws.com/0/58650/c22a3b2a-9fb7-d7c2-ba3c-6117cab7af88.png)


# スコープの設定
画面左側の[Client Scopes]をクリックし、画面右側の[Create]をクリックします。

![スクリーンショット 2018-10-21 13.08.56.png](https://qiita-image-store.s3.amazonaws.com/0/58650/abd27328-27d2-cce0-6181-f0e3f25d87cd.png)

[Client Scope Template]は[No template]にして[Next]をクリックします。

![スクリーンショット 2018-10-21 13.09.22.png](https://qiita-image-store.s3.amazonaws.com/0/58650/ff067d9d-0a62-a584-3aa5-c64c7e98bb76.png)

[Name]に `todo:read` と入力して[Save]をクリックします。

![スクリーンショット 2018-10-21 13.13.05.png](https://qiita-image-store.s3.amazonaws.com/0/58650/4cc71c9d-1f07-cc13-9406-b2e1eaf3123e.png)

同様の手順で、 `todo:write` スコープも設定します。最終的には下記のような状態になります。

![スクリーンショット 2018-10-21 13.14.53.png](https://qiita-image-store.s3.amazonaws.com/0/58650/20b65220-deae-b015-22a1-8ef55975b802.png)


# クライアントの設定
画面左側の[Clients]をクリックし、画面右側の[Create]をクリックします。

![スクリーンショット 2018-10-21 12.37.37.png](https://qiita-image-store.s3.amazonaws.com/0/58650/a8f4601e-9bc1-1ea7-72c6-4a7a95eb76b0.png)

[Client ID]に `todo-client` と、[Root URL]に `http://localhost:8080` と入力して[Save]をクリックします。

> この値は、[クライアントアプリケーションのapplication.yml](https://github.com/MasatoshiTada/oauth2-with-spring-security-51/blob/master/client/src/main/resources/application.yml#L10)で `spring.security.oauth2.client.registration.todo.client-id` に設定します。

![スクリーンショット 2018-10-21 12.44.05.png](https://qiita-image-store.s3.amazonaws.com/0/58650/c637a137-0c97-424f-28cf-1df0ca3e27a2.png)

[Settings]タブを開き、[Access Type]を[confidential]、[Valid Redirect URIs]に `http://localhost:8080/login/oauth2/code/todo-client` と入力して、[Save]をクリックします。

> この値は、[クライアントアプリケーションのapplication.yml](https://github.com/MasatoshiTada/oauth2-with-spring-security-51/blob/master/client/src/main/resources/application.yml#L13)で `spring.security.oauth2.client.registration.todo.redirect-uri` に設定します。

![スクリーンショット 2018-10-21 12.48.16.png](https://qiita-image-store.s3.amazonaws.com/0/58650/456033ac-7823-d046-a993-7c442d9ab947.png)

![スクリーンショット 2018-10-21 12.48.37.png](https://qiita-image-store.s3.amazonaws.com/0/58650/4f06484c-d954-7183-3d7c-b7024f31319d.png)

[Credentials]タブを開き、[Secret]の値をコピーします。

> この値は、[クライアントアプリケーションのapplication.yml](https://github.com/MasatoshiTada/oauth2-with-spring-security-51/blob/master/client/src/main/resources/application.yml#L11)で `spring.security.oauth2.client.registration.todo.client-secret` に設定します。

![スクリーンショット 2018-10-21 12.51.57.png](https://qiita-image-store.s3.amazonaws.com/0/58650/04948d60-276c-6348-8ae9-69b06a0ca7a4.png)

[Client Scopes]タブを開き、[Default Client Scopes]で `todo:read` と `todo:write` を選択して[Add selected]をクリックします。2つのスコープが[Assigned Default Client Scopes]に移動すれば成功です。

![スクリーンショット 2018-10-21 13.17.37.png](https://qiita-image-store.s3.amazonaws.com/0/58650/9f314be0-d112-3c57-758b-2811a4dc43d4.png)


# クライアントとリソースサーバーを起動してテスト
GitHubからソースコードをクローンします。

```bash
$ git clone https://github.com/MasatoshiTada/oauth2-with-spring-security-51.git
```

clientフォルダに移動して、クライアントを起動します。

```bash
$ cd oauth2-with-spring-security-51/client
$ mvn clean package
$ java -jar target/client-0.0.1-SNAPSHOT.jar
```

別のターミナル（Windowsの場合はコマンドプロンプト）を開いて、リソースサーバーを起動します。

```bash
$ cd oauth2-with-spring-security-51/resource-server
$ mvn clean package
$ java -jar target/resource-server-0.0.1-SNAPSHOT.jar
```

Webブラウザで http://localhost:8080/ にアクセスします。そうすると、[OAuth2ログインページ]にリダイレクトされます。

![スクリーンショット 2018-10-21 13.26.10.png](https://qiita-image-store.s3.amazonaws.com/0/58650/99384991-f3e4-fb31-ec38-9f3c9b23c4f1.png)

[Keycloakでログイン]をクリックすると、Keycloakのログイン画面にリダイレクトされるので、Keycloakに設定したユーザーでログインします。

![スクリーンショット 2018-10-21 13.27.47.png](https://qiita-image-store.s3.amazonaws.com/0/58650/a0ddcf5d-a6e7-9514-288c-0d131f3e95cf.png)

すると、クライアントに再リダイレクトされ、TODO一覧が表示されます。

![スクリーンショット 2018-10-21 13.28.11.png](https://qiita-image-store.s3.amazonaws.com/0/58650/c4ce19e9-264b-99b9-910a-ffa0f62ce6f8.png)

Keycloak管理画面で画面左側の[Users]をクリックし、[View all users]をクリックしてuserの[ID]をクリックします。

![スクリーンショット 2018-10-21 13.31.58.png](https://qiita-image-store.s3.amazonaws.com/0/58650/0d02fc92-7ff3-116e-26ae-d5a791930c84.png)

[Sessions]タブをクリックすると、現在Keycloakにログイン中のセッションが表示されます。

> 各セッションの[Logout]をクリックすると、そのセッションを強制破棄することができます。

![スクリーンショット 2018-10-21 13.33.04.png](https://qiita-image-store.s3.amazonaws.com/0/58650/568b7fcb-a5db-ea7f-fd7e-d00cca2c9c51.png)

![スクリーンショット 2018-10-21 13.33.58.png](https://qiita-image-store.s3.amazonaws.com/0/58650/7a15e17a-e08e-5318-206b-79c6f2b23b62.png)

TODOの登録も出来ます。

![スクリーンショット 2018-10-21 13.29.23.png](https://qiita-image-store.s3.amazonaws.com/0/58650/f88c5b9f-e4bd-148e-dd45-7aae3cd4b81c.png)

![スクリーンショット 2018-10-21 13.29.56.png](https://qiita-image-store.s3.amazonaws.com/0/58650/ddc0b4e0-9dcc-a022-6f30-35f04c0e79b4.png)

[ログアウト]をクリックするとクライアントとKeycloakの両方からログアウトします。

![スクリーンショット 2018-10-21 13.35.31.png](https://qiita-image-store.s3.amazonaws.com/0/58650/4d95717c-ef4c-ca4b-15ec-596822e47e25.png)

![スクリーンショット 2018-10-21 13.35.52.png](https://qiita-image-store.s3.amazonaws.com/0/58650/7342ead8-3ece-9c75-70ca-bc89c1211c31.png)

> Keycloakからもログアウトする方法は[こちらの記事](https://qiita.com/suke_masa/items/e04880c5cf7232b60004)参照。ソースコードは[このへん](https://github.com/MasatoshiTada/oauth2-with-spring-security-51/blob/master/client/src/main/java/com/example/client/security/keycloak/KeycloakService.java#L60)です。