KeycloakとSAMLを用いたシングルサインオン可能なSpringアプリケーションの実装
SpringアプリケーションでのKeycloakを利用したSAMLによるシングルサインオン実装方法を説明する。
目次
1.SAML認証とは
2.Keycloakの設定
3.SAML認証のためのライブラリインストール
4.SAML認証実装
5.動作確認
1.SAML認証とは
SAML(Security Assertion Markup Language)は、シングルサインオン(SSO)を実現する際に使用されるプロトコルの一つ。
このプロトコルは、異なるアプリケーション間での認証情報の共有を可能にし、そのためにSAML Assertionと呼ばれるXML形式のデータを使用する。
主に企業内のアプリケーション間で認証とSSOを実現する際に用いられ、セキュアな方法でユーザーの認証情報をアプリケーション間で共有する役割を果たす。
【参考】
◎ SSO認証の仕組みやフロー
◎ SAMLとは
●SAML認証フロー(SP-Initiated)

2.Keycloakの設定
2-1.下記URLからzipファイルダウンロードする。
◎ keycloak

2-2.ダウンロードしたZIPファイルを展開し、適当な場所に配置する。

2-3.コマンドプロンプトを起動して、binフォルダ下まで移動し、下記コマンドを実行する。
<Windowsの場合>
下記コマンドを実行
◎参考
kc.bat start-dev --http-port 8081 --log="file" --log-level="INFO"

<Linux系の場合>
下記コマンドを実行
./kc.sh start-dev --http-port 8081 --log="file" --log-level="INFO"

コマンド実行により、localhostの8081番ポートでkeycloakが起動する。
また、ログは『\keycloak-22.0.1\data\log』に記録される。

2-4.localhost:8081にアクセスし、管理者アカウントを作成する。 ※「8081」は起動指定したポート番号
UsernameとPasswordを入力して、Createをクリック

Administration Consoleをクリック

2-5.ログイン画面が出てくるので、先ほど作った管理者アカウントでログイン

2-6.レルムを作成する。

任意の名前を付けて保存する。

2-7.ロールを作成する。
作成したレルムのRealm rolesタブへ遷移し、Create roleを クリック

適当なロール名を入力して保存する

2-8.ユーザーを作成する。
作成したレルムのUsersタブへ遷移し、Add userを クリック

適当なユーザー名を入力して保存。

作成したユーザーをクリック

Role mapping タブを開き、Assign role をクリック

先ほど作成したロールを選択し、Assignする

この操作により、ユーザーにロールが割り当てられる。
3.SAML認証のためのライブラリインストール
3-1.pom.xmlの「<dependencies></dependencies>」内に下記ライブラリを5つ追記する。
:
<dependencies>
:
<!-- ここから -->
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- SAML導入 -->
<!-- https://mvnrepository.com/artifact/org.opensaml/opensaml-core -->
<dependency>
<groupId>org.opensaml</groupId>
<artifactId>opensaml-core</artifactId>
<version>4.3.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.opensaml/opensaml-saml-api -->
<dependency>
<groupId>org.opensaml</groupId>
<artifactId>opensaml-saml-api</artifactId>
<version>4.3.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.opensaml/opensaml-saml-impl -->
<dependency>
<groupId>org.opensaml</groupId>
<artifactId>opensaml-saml-impl</artifactId>
<version>4.3.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-saml2-service-provider -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-saml2-service-provider</artifactId>
<version>6.1.2</version>
</dependency>
<!-- ここまで -->
:
</dependencies>
3-2.org.opensamlの依存関係がエラーとなる場合は、<dependencies>の上に下記を追記する。
:
<!-- ここから -->
<repositories>
<repository>
<id>saml-repository</id>
<name>saml-repository</name>
<url>https://build.shibboleth.net/nexus/content/repositories/releases/</url>
</repository>
</repositories>
<!-- ここまで -->
<dependencies>
:
3-3.ライブラリをインストールする。
①プロジェクトを右クリック>[実行]>[Maven clean]
②プロジェクトを右クリック>[Maven]>[プロジェクトの更新]
③プロジェクトを右クリック>[実行]>[Maven install]
4.SAML認証実装
4-1.SpringSecurityの認証・認可設定ファイル作成

package com.example.demo.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
/*
* 認可設定とログインの設定を定義
*/
@Bean
public SecurityFilterChain filterChain(HttpSecurity http, HandlerMappingIntrospector introspector) throws Exception{
MvcRequestMatcher.Builder mvcMatcherBuilder = new MvcRequestMatcher.Builder(introspector);
http
.authorizeHttpRequests(authz -> authz
// URLごとの認可設定
// 認証したユーザのみがアクセス可能な画面として、"/top"を用意しておく
.requestMatchers(mvcMatcherBuilder.pattern("/top*")).hasAuthority("ROLE_USER")
.anyRequest().permitAll()
).saml2Login(saml2Login -> saml2Login
// SAML 2.0プロトコルの認証設定をカスタマイズする場合は、ここにCustomizerを指定します
.defaultSuccessUrl("/top") // 認証成功後のデフォルトのリダイレクト先を設定する場合など)
).saml2Metadata(
Customizer.withDefaults()
).csrf(
// CSRFキー有効化設定(デフォルト:有効)
Customizer.withDefaults()
);
return http.build();
}
}
.requestMatchers() | .hasAuthority(“ROLE_USER”):ロールuserを持つユーザのみアクセス可能。 .authenticated():認証済みのユーザであれば誰でもアクセス可能。 |
.saml2Login(saml2Login -> saml2Login) | SAML認証を行うよう設定する |
.saml2Metadata() | この設定により、metadataが作成される。 ここで作成されたmetadataをkeycloakに渡すことで トラストサークルも設定できる。 |
4-2.トラストサークルを設定するための証明書と秘密鍵を作成する。
トラストサークルは、IdPとSPが互いに同じ『証明書と秘密鍵』を持つことで成り立つ。サーバ証明書を使うことにより送信先の成りすましが防止される。秘密鍵での署名により、データの完全性を確保することができる。
証明書:データの送信者の正当性を証明するための電子的な証明書。個人や組織の識別情報と公開鍵を含む。
証明書は公開される情報であるため、第三者による不正利用を防ぐために慎重に保管する必要がある。
秘密鍵:データの署名に使用される。署名をすることで、そのデータが改ざんされていないことを確認できる。
秘密鍵は絶対的に秘密に保管される必要がある。他の人が秘密鍵にアクセスできれば、その人は署名や解読が可能となってしまう。
通信の際には、証明書によるデータの署名を通じて、情報の送信者が本物であることと、データが改ざんされていないことを確認する。
コマンドプロンプトを起動して、下記コマンドを実行する。
openssl req -newkey rsa:2048 -nodes -keyout local.key -x509 -days 365 -out local.crt
実行すると、Country Name等[個人や組織の識別情報]の入力を要求される。今回はテストなので、何も入力せずEnterでOK。


コマンドが正常に実行されると、local.keyとlocal.crtが作成される。
次の手順から、ここで作成した証明書と秘密鍵を共有することでトラストサークルを構築していく。
4-3.application.ymlにSAML認証の各種設定を記載する。
spring:
security:
saml2:
relyingparty:
registration:
my-client: #任意の値
entity-id: my-client #ここで指定した値がkeycloakのクライアントIDとなる
signing:
credentials:
- private-key-location: classpath:credentials/local.key #生成した秘密鍵と証明書を指定
certificate-location: classpath:credentials/local.crt #ファイルはsrc/main/resources/credentialsに配置しておく
assertingparty:
# keycloakのmetadataを取得するためのURL
# [keycloakホスト]/realms/[レルム名]/protocol/saml/descriptor
metadata-uri: http://localhost:8081/realms/Hello_keycloak/protocol/saml/descriptor

4-4.アプリケーションを起動し、keycloakに渡すためのmetadataを取得する。
起動して下記URLにアクセスするとsaml-my-client-metadata.xmlがダウンロードされる
※証明書と秘密鍵の情報を含むメタデータを生成
※”my-client”はymlで設定した値
http://localhost:8080/saml2/service-provider-metadata/my-client
4-5.ダウンロードしたmetadataからkeycloakのクライアントを作成する。
Keycloakで作成したレルムのClientsタブへ遷移し、Import clientをクリックする。

先ほどダウンロードしたmetadataをインポートする。


作成されたクライアントの設定値例


5.動作確認
下記の番号はすべて「●SAML認証フロー」に対応している。
Spring Applicationにアクセス
http://localhost:8080/top





