📝 BLOG.IK.AM

@making's memo
(🗃 Categories 🏷 Tags)

Spring Boot 1/2のアプリにKeycloakのOpenID Connectを使ってシングルサインオン

🗃 {Programming/Java/org/springframework/security/oauth2}

🏷 Java 🏷 Keycloak 🏷 OpenID Connect 🏷 Spring 🏷 Spring Boot 🏷 Spring Security

🗓 Updated at 2018-01-16T18:26:05+09:00 by Toshiaki Maki  🗓 Created at 2018-01-16T16:30:37+09:00 by Toshiaki Maki  {✒️️ Edit  ⏰ History}


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

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

keycloak-spring-boot-starterに関しては、RedHatのブログが参考になります。

目次

クライアントの定義

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

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

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

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

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

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

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機能を使用できます。

Spring InitializrでDependenciesに"Web"と"Cloud OAuth2"を選択します。

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

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

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アノテーションをつけ、

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を取れるようになります。

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にアクセスすると、Keycloakのログイン画面にリダイレクトされます。

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

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

この辺を見てください。

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

Spring Boot 1の場合は、Spring Security OAuth Workshopの内容が活かせます。

Spring Boot 2の場合

Spring Boot 2はSpring Security 5のOAuth 2.0 Login機能をそのまま使用できます。

Spring InitializrでSpring Bootのバージョンを2.0.0.M7にし、Dependenciesに"Web"と"Security"を選択します。

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

<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に含まれていないのでフル定義が必要です。

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

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

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に変え、返り値も少し変えます。

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();
    }
}

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

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

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

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


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

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