---
title: Spring Boot 1/2のアプリにKeycloakのOpenID Connectを使ってシングルサインオン
tags: ["Java", "Spring", "Spring Boot", "Spring Security", "Keycloak", "OpenID Connect"]
categories: ["Programming", "Java", "org", "springframework", "security", "oauth2"]
date: 2018-01-16T16:30:37Z
updated: 2018-01-16T18:26:05Z
---

KeycloakとSpring Bootを連携するために、RedHatより[`org.keycloak:keycloak-spring-boot-starter`](https://github.com/keycloak/keycloak/tree/master/misc/spring-boot-starter)というSpring Boot用のStarterライブラリが提供されていますが、これはKeycloakのAPIを使っています。Spring BootとKeycloakを合わせて検索するとだいたいこのライブラリが使われています。

KeycloakをOpenID Connectという標準仕様を使ってアクセスするのに、ベンダ提供ライブラリを使うとアプリケーションのポータビリティが損なわれるので、この記事ではSpringのOAuth2/OpenID Connect機能のみを使った連携方法を紹介します。

> `keycloak-spring-boot-starter`に関しては、[RedHatのブログ](https://developers.redhat.com/blog/2017/05/25/easily-secure-your-spring-boot-applications-with-keycloak/)が参考になります。

**目次**

<!-- toc -->

### クライアントの定義

まずはKeycloak側にクライアントを作成します。

`demo` Realmにクライアントを登録します。

サイドバーの"Clients"をクリックして、右の"Create"ボタンをクリック。

<img src="https://user-images.githubusercontent.com/106908/34990674-47f1e52e-fb0a-11e7-90aa-6a857593d321.png" width="400px">

"Client ID"に`demo`を、"Root URL"に"http://localhost:8080"(これから作成するSpring BootアプリのURL)を入力し、"Save"ボタンをクリック。

<img src="https://user-images.githubusercontent.com/106908/34991033-862d177c-fb0b-11e7-80fa-4efa27852f88.png" width="400px">

"Access Type"を"confidential"に変更して、"Save"ボタンをクリック。

<img src="https://user-images.githubusercontent.com/106908/34992721-640ad41c-fb11-11e7-8d9a-7d201804d6e8.png" width="400px">

"Clients"から`demo`クライアントを選択すると"Credentials"タブが増えているので、クリックすると"Secret"(Client Secret)が表示されているので、これをコピー。

<img src="https://user-images.githubusercontent.com/106908/34992761-879e6e48-fb11-11e7-9640-35048e17ccdc.png" width="400px">


### Spring Bootでクライアントを実装

Spring Security 5でOpenID Connect対応が入ったため、Spring Boot 1.5と2以降で大きく設定方法が変わります。

#### Spring Boot 1.5の場合

Spring Boot 1.5 (Spring Security 4)の場合は、Spring Security OAuth2を使って、Spring Bootの[Single Sign On機能](https://docs.spring.io/spring-boot/docs/1.5.9.RELEASE/reference/html/boot-features-security.html#boot-features-security-oauth2-single-sign-on)を使用できます。

[Spring Initializr](https://start.spring.io)でDependenciesに"Web"と"Cloud OAuth2"を選択します。

> でDependenciesにに"Keycloak"を選ぶ必要はありません。


<img src="https://user-images.githubusercontent.com/106908/34993860-04c32302-fb15-11e7-9bdb-0a4c19711656.png" width="400px">


`application.properties`に次の内容を設定。`security.oauth2.client.client-secret`には"クライアントの定義"で生成された"Client Secret"を設定してください。

``` properties
keycloak.auth-server-url=<KeycloakのURL>/auth
keycloak.realm=demo
keycloak.base.uri=${keycloak.auth-server-url}/realms/${keycloak.realm}/protocol/openid-connect
security.oauth2.client.client-id=demo
security.oauth2.client.client-secret=dd3cb803-4151-45d5-92b9-230be9813f40
security.oauth2.client.scope=openid
security.oauth2.client.access-token-uri=${keycloak.base.uri}/token
security.oauth2.client.user-authorization-uri=${keycloak.base.uri}/auth
security.oauth2.resource.user-info-uri=${keycloak.base.uri}/userinfo
security.oauth2.resource.jwk.key-set-uri=${keycloak.base.uri}/certs
```


メインのクラスに`@EnableOAuth2Sso`アノテーションをつけ、

``` java
package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;

@SpringBootApplication
@EnableOAuth2Sso
public class DemoSsoWithKeycloakApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoSsoWithKeycloakApplication.class, args);
    }
}
```

あとはControllerの引数にログインユーザー情報として`java.security.Principal`あるいは`org.springframework.security.core.Authentication`を取れるようになります。

``` java
package com.example.demo;

import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
    @GetMapping("/")
    public Object hi(Authentication authentication) {
        return authentication;
    }
}
```


`main`メソッドを実行して、[http://localhost:8080](http://localhost:8080)にアクセスすると、Keycloakのログイン画面にリダイレクトされます。

<img src="https://user-images.githubusercontent.com/106908/34992163-99d85c6a-fb0f-11e7-913c-5251918e42e6.png" width="400px">

`demo` Realmのユーザーでログインすると、ログインユーザー情報が表示されます。

<img src="https://user-images.githubusercontent.com/106908/34992110-6398c720-fb0f-11e7-89b0-4df85fab6048.png" width="400px">

ログインユーザーをカスタマイズしたい場合は、`org.springframework.boot.autoconfigure.security.oauth2.resource.PrincipalExtractor`と`org.springframework.boot.autoconfigure.security.oauth2.resource.AuthoritiesExtractor`を実装すれば良いです。

[この辺](https://www.slideshare.net/makingx/spring-boot-tips/85)を見てください。


Roleの設定は省略しました。
また、取得するScopeの同意画面やScopeの細かい設定は、"Consent Required"を"ON"にして、"Scope"や"Mappers"タブを調整する必要があります。


> Spring Boot 1の場合は、[Spring Security OAuth Workshop](https://github.com/Pivotal-Japan/spring-security-oauth-workshop)の内容が活かせます。

#### Spring Boot 2の場合

Spring Boot 2は[Spring Security 5のOAuth 2.0 Login機能](https://docs.spring.io/spring-security/site/docs/5.0.0.RELEASE/reference/htmlsingle/#jc-oauth2login)をそのまま使用できます。

[Spring Initializr](https://start.spring.io)でSpring Bootのバージョンを`2.0.0.M7`にし、Dependenciesに"Web"と"Security"を選択します。

<img src="https://user-images.githubusercontent.com/106908/34997620-48715a46-fb20-11e7-8587-4d5a0eeb85f3.png" width="400px">

OAuth2対応に必要なSpring Securityのライブラリは`spring-boot-starter`に含まれておらず、`2.0.0.M7`の段階ではStarterライブラリも用意されていないので、次のライブラリを`pom.xml`の`<dependencies>`に追加する必要があります。

``` xml
<dependency>
	<groupId>org.springframework.security</groupId>
	<artifactId>spring-security-oauth2-client</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.security</groupId>
	<artifactId>spring-security-oauth2-jose</artifactId>
</dependency>
```


Keycloakの情報を`application.properties`に定義します。Keycloakは[`CommonOAuth2Provider`](https://github.com/spring-projects/spring-security/blob/master/config/src/main/java/org/springframework/security/config/oauth2/client/CommonOAuth2Provider.java)に含まれていないのでフル定義が必要です。


``` properties
keycloak.auth-server-url=<KeycloakのURL>/auth
keycloak.realm=demo
keycloak.base.uri=${keycloak.auth-server-url}/realms/${keycloak.realm}/protocol/openid-connect

spring.security.oauth2.client.provider.keycloak.token-uri=${keycloak.base.uri}/token
spring.security.oauth2.client.provider.keycloak.authorization-uri=${keycloak.base.uri}/auth
spring.security.oauth2.client.provider.keycloak.user-info-uri=${keycloak.base.uri}/userinfo
spring.security.oauth2.client.provider.keycloak.jwk-set-uri=${keycloak.base.uri}/certs
spring.security.oauth2.client.provider.keycloak.user-name-attribute=preferred_username

spring.security.oauth2.client.registration.keycloak.client-name=Keycloak
spring.security.oauth2.client.registration.keycloak.client-authentication-method=basic
spring.security.oauth2.client.registration.keycloak.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.keycloak.redirect-uri-template={baseUrl}/login/oauth2/code/{registrationId}

spring.security.oauth2.client.registration.keycloak.client-id=demo
spring.security.oauth2.client.registration.keycloak.client-secret=dd3cb803-4151-45d5-92b9-230be9813f40
spring.security.oauth2.client.registration.keycloak.scope=openid
```

メインのクラスには特に何も設定する必要はありません。

``` java
package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoSsoWithKeycloak2Application {

	public static void main(String[] args) {
		SpringApplication.run(DemoSsoWithKeycloak2Application.class, args);
	}
}
```

`HelloController`の引数は`OAuth2AuthenticationToken`に変え、返り値も少し変えます。

``` java
package com.example.demo;

import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
    @GetMapping("/")
    public Object hi(OAuth2AuthenticationToken authentication) {
        return authentication.getPrincipal().getAttributes();
    }
}
```

`OAuth2AuthenticationToken`は`Authentication`の実装クラスなのですが、JSONシリアライズできないので、シリアライズのできる`principal.attributes`を返します。

`main`メソッドを実行して、[http://localhost:8080](http://localhost:8080)にアクセスすると、クライアント選択画面にリダイレクトされます。

<img src="https://user-images.githubusercontent.com/106908/34997383-beb8e74c-fb1f-11e7-97ac-84c49e514ad5.png" width="400px">

"Keycloak"をクリックすると、Keycloakのログイン画面にリダイレクトされます。

<img src="https://user-images.githubusercontent.com/106908/34997473-ec869368-fb1f-11e7-8181-4bd53b93658d.png" width="400px">

`demo` Realmのユーザーでログインすると、ログインユーザー情報が表示されます。

<img src="https://user-images.githubusercontent.com/106908/34997525-0b3cd272-fb20-11e7-9f95-c827214195ef.png" width="400px">


----

Spring BootとKeycloakの連携について説明しました。
というか、Keycloakはあまり関係なくて、一般的なOpenID Connect連携の方法でした。

個人的にはKeycloakのライブラリを使うより、こっちが好みです。
