KeycloakとSpring Bootアプリを連携してみる

:::note info
本記事は、Keycloakのバージョンアップに伴い、
[「KeycloakでOpenID Connectを使ってみる（Spring Bootアプリケーション編）」](https://qiita.com/tamura__246/items/4290a1035e1adcab733b)
の記事の内容を最新化・拡充したものです。
:::


# 概要

Spring BootベースのWebアプリケーションの認証をKeycloakに委譲する手順について説明します。Keycloakにはい以前はSpring Boot用のアダプタがありましたが、deprecatedとなっているため、今回はSpring BootベースのWebアプリケーションにSpring Securityを入れ、認証をKeycloakへ委譲できるようにしたいと思います。これにより、最新の認証プロトコルである「OpenID Connect」(以下OIDC)に対応したセキュアなSpring Bootアプリケーションとなります。

連携ができると、ユーザーはOIDCの「End User」に、Spring BootベースのWebアプリケーションはOIDCの「Relying Party」に、KeycloakはOIDCの「OpenID Provider」になります。

# はじめる前に
このチュートリアルの操作をはじめる前に、Keycloakのセットアップを完了し、管理者ユーザーを作成する必要があります。こちらの記事を参考にそれらの作業を実施しておいて下さい。また、Webアプリケーションのビルドと起動にGradleかMavenを使用しますので、いずれかをインストールしておいて下さい。



# 連携の手順
連携の手順は以下のようになります。これが完了したら、動作確認をします。
    Keycloakの設定
        ・1.1. レルムの作成
        ・1.2. クライアントの作成
        ・1.3. ロールの作成
        ・1.4. ユーザーの作成とロールの割り当て
    簡単なSpring Bootアプリケーションの作成
        ・2.1. Spring Initializrでアプリの雛形作成
        ・2.2. クラスとhtmlの作成
        ・2.3. アプリのKeycloak連携の設定

## 1.1. レルムの作成
レルムを作成する前にレルムについて簡単に説明します。
Keycloakにおける「レルム(Realm)」とは、ユーザ、認証、セッション管理、アクセスコントロールなどのセキュリティ関連情報をまとめた単位を意味します。ユーザは、レルム内に作成することができ、認証の方法などをレルム単位で定義することができます。デフォルトでは、「Master」というレルムが一つあり、その後追加する全てのレルムを包含して管理することができます。「Master」レルムは、レルムの階層内で最も高いレベルであり、スーパー管理者(初回セットアップ時に作成した管理者アカウント)で管理できます。

「Master」レルムを使ってユーザーなどを管理することもできますが、基本的にはレルムを作成することが推奨されているので、まずはレルムを作成します。なお、「Master」レルムは削除することもできます。
http://localhost:8080/admin/
にアクセスして、管理者アカウントでKeycloak管理コンソールにログインします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3341882/a5cd9538-f136-bb25-397f-e1978360cf5b.png)

左側の一番上コーナーにある「Master」と表示されたドロップダウン・メニューで、「Create Realm」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3341882/bfaa9875-2f8f-5aa3-751d-b1914a9ad909.png)

レルム追加ページが開きます。新規レルムを作成するので、レルム名に「demo」と入力して「Create」ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3341882/a4c3a3db-499c-79df-e7ed-f0222c3ef3e7.png)

レルムを作成すると、管理コンソールのメインページに遷移します。現在のレルムは「demo」となっているはずです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3341882/f3390199-789b-bc1e-4427-f908b19c7c73.png)
左上隅のドロップダウン・メニューをクリックして、管理下にあるmasterレルムと今作成したレルムを切替えることができます。

## 1.2. クライアントの作成

次に、クライアントを作成します(※)。今回のケースにおいて、クライアントとは、OIDCで規定された「Relying Party」(OIDC 1.0のベースとなっているOAuth 2.0では「クライアント」とも言います)であり、Spring Bootアプリケーションのことになります。

<table><tr><td>※正確に言うと、クライアントのプロファイルを作成します。クライアント自体は、後述する「Spring Initializr」などを使って作成します。</td></tr></table>

左メニューバーで「Clients」をクリックします。クライアントの一覧画面が表示されるので、「Create client」ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3341882/0a2ab5df-61cb-9508-c58f-bbe5530e8887.png)


「demo-app」と入力して、「Next」ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3341882/baaed9e6-1207-57c1-4837-0d713b6e97f2.png)

Client authenticationをOnにします。それ以外の項目は変更せず、「Next」ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3341882/16d2c42f-6197-bdd3-826f-2a4e93b08806.png)


「Valid redirect URIs」に「`http://localhost:8081/*`」と入力し、「Save」ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3341882/3bebf6ea-d8c8-9aeb-0949-3664615d56e4.png)

## 1.3. ロールの作成

次に、ユーザーに割り当てるロールを作成します。このロールが割り当てられたユーザーだけが、Spring Bootアプリケーションにアクセスできるように、後で設定を行います。

<table><tr><td>ロールは、ユーザーの役割を意味し、ユーザーのタイプやカテゴリを識別するために利用します。管理者、ユーザー、マネージャー、従業員などが、組織内に存在する典型的なロールです。アプリケーションでは、ユーザーの管理が難しくならないように、個々のユーザーではなく特定のロールにアクセス権を割り当てることがよくあります。</td></tr></table>

左メニューバーで「Realm roles」をクリックします。ロールの一覧画面が表示されるので、「Create Role」ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3341882/2e749618-70d1-088d-85eb-e005e9efc4d5.png)


「ロール名」に「user」と入力して「Save」ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3341882/a7211102-4c40-d979-2ee1-a9e12c02f2e7.png)


## 1.4. ユーザーの作成とロールの割り当て

最後に、ユーザーを作成し、ロールを割り当てます。左メニューバーで「Users」をクリックすると
ユーザーの一覧画面が表示されるので、「Add user」ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3341882/d750064a-5bb1-e827-cff3-85096efe9e09.png)

必須入力のユーザー名のみを入力して、「Create」ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3341882/d9ad8aee-6f78-f3b0-f36e-9129feb79c6e.png)

新規ユーザーの管理ページが開けば、ユーザ作成ができています。
次に、新規ユーザーのパスワードを設定します。「Credentials」タブをクリックすると
以下の画面が表示されるので、「Set password」ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3341882/96415e00-ad93-afa9-41fe-77ea209fe11f.png)


新しいパスワードとパスワード(確認)を入力し「Save」ボタンをクリックします。この時、「Temporary」は「Off」に変更して下さい。これにより、初回ログイン時にパスワードを変更する必要がなくなります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3341882/9195f3f3-6303-63d5-6836-6a889cc5328c.png)

確認画面がでるので、「Save password」をクリックし、「user001」のパスワードが設定できます。

最後に「Role mapping」のタブから、「Assign role」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3341882/be9257bb-6d3b-d1cb-02de-ebde2348d37b.png)
先ほど「1.3. ロールの作成」で作成した`user`ロールを「User001」にAssignします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3341882/50af422c-618a-4d1e-cbb7-e3c581d8faba.png)


以上でKeycloakの設定は完了です。

## 2.1. Spring Initializrでアプリの雛形作成

次に、Spring Bootアプリケーションを作成します。Spring Bootアプリケーションの雛形作成には、Spring Initializrを使用します。[Spring Initializr](https://start.spring.io/)のページにアクセスして下さい。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3341882/bb0759fa-c335-c82a-7d65-fea45628b9b8.png)


ここでは以下のように入力、選択してから、「Generate Project」ボタンをクリックでプロジェクトを作成します。

|項目|設定値|
|---|---|
|Project|Gradle - Groovy|
|Language|Java|
|Dependencies|Spring Web、Thymeleaf、DevTools、Spring Security|
|Group|com.example|
|Artifact|sample-app|

圧縮ファイルがダウンロードされるので、適当なディレクトリに解凍して下さい。

## 2.2. クラスとhtmlの作成

まずはトップページの静的なHTMLを作成します。`src/main/resources/static/`に`index.html`というHTMLファイルを次のような内容で作成します。

```html
<html>
<head>
    <title>Demo Application</title>
</head>
 <body>
   <h1>Springboot Demo Application</h1>
   <a href="/home">Go to home page!（要認証ページ）</a>
 </body>
</html>
```

次にこの画面から呼び出されるコントローラと画面を作成します。コントローラは、`src/main/java/com/example/demo/`に`demoApplication.java`というファイル名で次のような内容とします。

```java
package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;

@RestController
@SpringBootApplication
public class demoApplication {

    @RequestMapping("/home")
    public String home() {
        return "Hello World!";
    }

    public static void main(String[] args) {
        SpringApplication.run(demoApplication.class, args);
    }
}
```
次にSpring Securityの設定を行います。以下では`index.html`のページに関しては認証がいらず、`/home`のページに関しては認証が必要となる旨の記載を行っています。Spring Securityによる設定の仕方はこの方法に限らず、xmlでも行えます。詳細はSpring Securityの[公式ページ](https://docs.spring.io/spring-security/reference/index.html)を参照ください。
```java
package com.example.demo.config;

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.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import java.util.Arrays;

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    public SecurityFilterChain web(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(authorize -> authorize
            .requestMatchers(AntPathRequestMatcher.antMatcher("/index.html")).permitAll()
            .anyRequest().authenticated()).oauth2Login(); 
        return http.build();
    }
}
```
これで非常にシンプルですが、だれでもアクセスが可能な`index.html`のページと認証が必要な`/home`のページを持つサンプルが完成しました。

## 2.3. アプリのKeycloak連携の設定

最後に、作成したWebアプリケーションにKeycloakと連携するための設定を行います。`/src/main/resources/application.yml`に以下の定義を追加してください。

```properties
server:
 port: 8081
spring:
 security:
   oauth2:
     client:
       registration:
         keycloak:
           client-id: demo-app
           client-secret:{対応するClientsのCredentilasタブから取得したシークレットキー}
           provider: keycloak
           scope: openid
           authorization-grant-type: authorization_code
           redirect-uri: '{baseUrl}/login/oauth2/code/{registrationId}'
       provider:
         keycloak:
           authorization-uri: http://localhost:8080/realms/demo/protocol/openid-connect/auth
           token-uri: http://localhost:8080/realms/demo/protocol/openid-connect/token
           user-info-uri: http://localhost:8080/realms/demo/protocol/openid-connect/userinfo
           jwk-set-uri: http://localhost:8080/realms/demo/protocol/openid-connect/certs
           user-name-attribute: preferred_username
```

最初のプロパティは、このアプリケーションがポート8081でリッスンされることを指定しています。 
あとは、Spring Securityから処理を委譲する際に必要な情報を登録します。URL内の`demo`についてはレルム名です。またシークレットキーに該当する部分についてはKeycloakの以下から取得します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3341882/9cc8e4b5-1bdc-72db-6cca-0ece61cc14b3.png)


以上で全ての設定は完了です。

# 動作確認

では、動作確認してみましょう。次のコマンドで作成したWebアプリケーションを起動します。

Gradleの場合

```bash
$ gradle bootRun
```

Mavenの場合

```bash
$ mvn spring-boot:run
```

[http://localhost:8081/index.html](http://localhost:8081/index.html)にアクセスすると、次のような画面が表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3341882/a588a0ad-0be7-5dfe-c8c9-12d5c47a475d.png)

「Go to home page!」のリンクをクリックすると、Keycloakのログイン画面にリダイレクトされます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3341882/44cfc64f-f600-698b-7f77-64a0ca1cf53b.png)

作成したユーザーでログインして下さい。これまでの設定が適切にできていれば、Spring Securityにて設定を行った認証が必要な画面（ここではただHello Worldを表示している画面)が表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3341882/191b4b5e-755e-d471-ae1f-32fc9164d442.png)


# 最後に

今回は検証のため、簡易的なアプリと認証の処理をKeycloak側へ委譲させる部分だけを実装しましたが、Spring BootベースのWebアプリケーションの認証で、標準的でセキュアなログイン処理を比較的簡単に実装することができました。

また、ここでは記載していないですが、属性やロールベースによる権限制御もSpring Securityと組み合わせたうえで実現するということを今後、別で記載できればと思います。

# 参考資料

 - [Keycloak Documentation](http://www.keycloak.org/documentation.html)
 - [NRI OpenStandia Keycloak日本語ドキュメント](http://keycloak-documentation.openstandia.jp/master/ja_JP/getting_started/index.html)
 - [Spring Security reference](https://docs.spring.io/spring-security/reference/index.html)