今日やること
Keycloakアドベンドカレンダー5日目の今回は、Tomcat 8用のクライアント・アダプター(OpenID Connect版)を試してみます。
クライアント・アダプターとは、KeycloakとID連携(SSO)し、アプリケーション保護を容易にするためのプラットフォーム固有のライブラリ群です。基本的にはアプリケーションが導入されているミドルウェア上にクライアント・アダプターをインストール/設定した上で、ユーザー連携のため多少のコードカスタマイズを実施することになります。
現在サポートされている主なプラットフォームおよびプロトコルは以下のとおりです。
- プラットフォーム
- Wildfly
- JBoss EAP
- JBoss AS
- JBoss Fuse
- Jetty
- Tomcat
- Javascript(クライアント)
- プロトコル
- OpenID Connect
- SAML 2.0
上記以外にも、コミュニティから提供されているクライアント・アダプターが複数あります。プラットフォームとプロトコルの組み合わせによってはサポートされていないものがあるので、詳しくは参考資料の"Supported Platforms"を参照ください。
今回はTomcat 8のサンプルアプリ(examples)に対してTomcat 8用のクライアント・アダプター(OpenID Connect版)を導入し、KeycloakとID連携を行う方法を試してみます。
動作確認用のサーバー構成
今回のお試し環境はこのような感じです。
Keycloakサーバーには、demo
レルムが作成されており(3日目の記事を参照)、ログイン可能なユーザーが存在していることが前提となります。
FQDN | OS | JDK | 構成 |
---|---|---|---|
kc-server.example.com | CentOS 7.4.1708 | OpenJDK 1.8.0.151 | ・Keycloakサーバー 3.3.0.CR2 |
kc-tomcat.example.com | CentOS 7.4.1708 | OpenJDK 1.8.0.151 | ・Tomcat 8.5.23(examplesアプリを利用) ・Tomcat 8用クライアント・アダプター(OpenID Connect版) 3.3.0.Final |
ユーザー | ロール |
---|---|
user001 | user |
admin001 | user,admin |
Tomcatに付属するexapmlesアプリケーションでは、以下のようなアクセス制御をかけることを想定します。
動作確認時の各サーバーへのアクセスポートは以下のとおりです。
Keycloakサーバー側の設定
クライアントの追加・設定
- Keycloakの管理コンソールにログインします。
- 左メニューバーから、
demo
レルムを選択します。 - 左メニューバーで
クライアント
をクリックします。クライアント一覧が表示されます。 - 右上の
作成
ボタンを押下します。 - 以下のようなクライアント設定値を入力し、
保存
ボタンを押下します
- クライアントID: kc-tomcat
- クライアントプロトコル: openid-connect
- ルートURL: http://kc-tomcat.example.com:8080
- 引き続き kc-tomcatの設定画面が表示されるので以下のような設定を行い、
保存
ボタンを押下します。
- アクセスタイプ: confidential
- 有効なリダイレクトURI(1つ目): http://kc-tomcat.example.com:8080/examples/
- 有効なリダイレクトURI(2つ目): http://kc-tomcat.example.com:8080/examples/websocket/echo.xhtml
- 有効なリダイレクトURI(3つ目): http://kc-tomcat.example.com:8080/examples/getUserInfo.jsp
有効なリダイレクトURI
は、Keycloak上ではワイルドカードによる指定も可能です。ただし、OIDCの仕様上では、redirect_uriと厳密に一致することが必須となっているため、ここではリダイレクト先となりえるURLはすべて指定しています。そのため、ここで指定していないURLへは未ログイン状態から直接遷移できません。
クライアント・アダプター側の設定
クライアント・アダプターの設定では、WAR単位で連携設定が必要です。
Webアプリケーション単位で、以下の3つの手順を実施します。
- $WAR_HOME/META-INF/context.xmlの配置
- $WAR_HOME/WEB-INF/keycloak.jsonの配置
- $WAR_HOME/WEB-INF/web.xmlの編集
以下、順を追って説明します。
Tomcat 8の導入
連携確認用のサンプルアプリケーションとして、Tomcat 8を導入します。
cd /tmp
wget http://ftp.tsukuba.wide.ad.jp/software/apache/tomcat/tomcat-8/v8.5.23/bin/apache-tomcat-8.5.23.tar.gz
tar zxvf apache-tomcat-8.5.23.tar.gz -C /usr/local
Tomcat 8用のクライアント・アダプターのダウンロードと展開
Tomcat 8用のクライアント・アダプターをダウンロードして /usr/local/apache-tomcat-8.5.23/lib/
に解凍します。
cd /tmp
wget https://downloads.jboss.org/keycloak/3.3.0.Final/adapters/keycloak-oidc/keycloak-tomcat8-adapter-dist-3.3.0.Final.tar.gz
tar zxvf keycloak-tomcat8-adapter-dist-3.3.0.Final.tar.gz -C /usr/local/apache-tomcat-8.5.23/lib/
META-INF/context.xml の配置
今回はTomcatのexamplesのWebアプリケーションに対して保護をかけるため、
対象のWARディレクトリ配下にMETA-INF/context.xml
を作成します。
この設定ではTomcatのValveとしてKeycloakAuthenticatorValve
を設定しています。Valveとは、リクエストをフックするためのTomcat独自のプラグイン機構です。KeycloakとID連携を行うためのKeycloakAuthenticatorValve
を設定することにより、当該アプリケーションへのリクエストが事前にフィルタされ、KeycloakとのID連携やアプリケーション保護に必要な処理が実施されるようになります。
mkdir -p /usr/local/apache-tomcat-8.5.23/webapps/examples/META-INF/
<Context path="/examples">
<Valve className="org.keycloak.adapters.tomcat.KeycloakAuthenticatorValve"/>
</Context>
WEB-INF/keycloak.json の配置
対象のWARディレクトリのWEB-INF
以下に、クライアント・アダプターの設定用のJSONファイル(keycloak.json)を配置します。
基本的に、Keycloakの管理画面で控えたjsonをそのまま設定するだけですが、今回はssl-required
をnone
に変更します。
{
"realm": "demo",
"auth-server-url": "https://kc-server.example.com:8443/auth",
"ssl-required": "none",
"resource": "kc-tomcat",
"credentials": {
"secret": "{Keycloakサーバー側のシークレットの値}"
}
}
パラメータ名 | 必須 | 説明 |
---|---|---|
realm | ○ | Keycloak側のレルム名を指定します。 |
auth-server-url | ○ | Keycloak側のベースURLを指定します。1 |
ssl-required | HTTPSでのアクセスを強制するかどうか指定します。"all", "external", "none" のいずれかを指定します。デフォルトは、"external"なので、外部からはHTTPのアクセスは許可されません。 | |
resource | ○ | Keycloak側のクライアント設定のクライアントID(client-id)を指定します。 |
credentials | △ | Keycloak側のアクセスタイプで"confidential"を指定した場合は必須です。 |
adapter-configの設定は非常に多くあるため、動作確認に必要な最低限の項目のみ記載しています。その他のパラメータに関しては、参考資料の"Java Adapter Config"を参照ください。
今回は、Tomcatに8080ポートでアクセス可能にするため、ssl-required
は、noneに変更しています。ただし、本来のOIDCではセキュリティ担保のためHTTPSの利用が前提となっています。そのため、実環境ではssl-required
はall
にすべきです。
WEB-INF/web.xmlの編集
examples/WEB-INF/web.xml に元から定義されている login-config
の設定があるため、こちらは一旦コメントアウトします。
・
・
<!-- Default login configuration uses form-based authentication -->
<!-- COMMENT OUT
<login-config>
<auth-method>FORM</auth-method>
<realm-name>Example Form-Based Authentication Area</realm-name>
<form-login-config>
<form-login-page>/jsp/security/protected/login.jsp</form-login-page>
<form-error-page>/jsp/security/protected/error.jsp</form-error-page>
</form-login-config>
</login-config>
-->
・
・
クライアント・アダプターによるID連携やアプリケーション保護はJavaEE標準のセキュリティ機能と連携するため、各アプリケーションにあわせてweb.xmlに固有の設定を行う必要があります。
今回のアクセス制御の設定例では、以下のように設定します。
- /examples/* へのアクセスはuserロールの権限が必要
- /examples/websocket/* へのアクセスはadminロールの権限が必要
・
・
<security-constraint>
<web-resource-collection>
<web-resource-name>User Area</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>user</role-name>
</auth-constraint>
</security-constraint>
<security-constraint>
<web-resource-collection>
<web-resource-name>Admin Area</web-resource-name>
<url-pattern>/websocket/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>admin</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>this is ignored currently</realm-name>
</login-config>
<security-role>
<role-name>admin</role-name>
</security-role>
<security-role>
<role-name>user</role-name>
</security-role>
・
・
Tomcatの起動
クライアント・アダプターの準備が整ったので、Tomcat起動します。
cd /usr/local/apache-tomcat-8.5.23/
./bin/startup.sh
動作確認
user ロールが必要なURL
-
userロールが必要なhttp://kc-tomcat.example.com:8080/examples/ にアクセスします。
-
Keycloakに未ログインであるため、ログイン画面にリダイレクトが発生します。
-
userロールを持つユーザーのID/PWでログインします。
-
adminロールが必要な http://kc-tomcat.example.com:8080/examples/websocket/echo.xhtml にアクセスします。
userおよびadminロールが必要なURL
-
userおよびadminロールが必要な http://kc-tomcat.example.com:8080/examples/websocket/echo.xhtml にアクセスします。
-
Keycloakに未ログインであるため、ログイン画面にリダイレクトが発生します。
-
user / admin の双方のロールを持つユーザーのID/PWでログインします。
-
http://kc-tomcat.example.com:8080/examples/websocket/echo.xhtml が表示されます。
-
userロールが必要なhttp://kc-tomcat.example.com:8080/examples/ にアクセスします。
ユーザー情報の取得確認
上記の確認では、アクセス制御の確認はできましたがログインしたユーザー情報をどのように取得したらよいのかよく分かりません。クライアント・アダプターを利用している場合は、KeycloakのAPIを利用することで、OIDCで連携されたIDトークンの情報を簡単に取得することができます。以下のサンプル実装では、HTTPセッションからユーザー情報を取得するJSPを新たに配置して、ログイン済みのユーザー情報が取得できるかどうか確認しています。
以下のgetUserInfo.jsp
をWARディレクトリ直下に配置します。
<%@ page import="org.keycloak.KeycloakSecurityContext" %>
<%@ page import="org.keycloak.representations.IDToken" %>
<%
// HTTPセッションからKeycloakSecurityContextを取得
KeycloakSecurityContext ksc = (KeycloakSecurityContext)session.getAttribute(KeycloakSecurityContext.class.getName());
// KeycloakSecurityContextから、IDTokenを取得
IDToken idToken = ksc.getIdToken();
%>
PREFERRED_USERNAME : <%=idToken.getPreferredUsername()%><br>
EMAIL: <%=idToken.getEmail() %><br>
GIVEN_NAME: <%=idToken.getGivenName()%><br>
FAMILY_NAME: <%=idToken.getFamilyName()%><br>
- http://kc-tomcat.example.com:8080/examples/getUserInfo.jsp にアクセスします。
- Keycloakに未ログインであるため、ログイン画面にリダイレクトが発生します。
- userロールを持つユーザーのID/PWでログインします。
- http://kc-tomcat.example.com:8080/examples/getUserInfo.jsp が表示されます。
- HTTPセッションからKeycloakのユーザー情報が取得できるのが分かります。
まとめ
今回はKeycloakとTomcat上のアプリケーションをID連携させる方法を確認しました。Tomcatとの連携に関しては、WAR内の設定ファイルを3つ追加/変更をするだけなので、比較的容易に組み込みが可能なクライアント・アダプターかと思います。
ただ、連携が必要なアプリケーションサーバーが多数ある場合や、そのプラットフォームが多種多様である場合は、それぞれのサーバーで導入/設定を実施していく必要があるので、作業が煩雑になる可能性があります。
このような構成の場合は、8日目の記事で私が解説する「Keycloakでリバプロ型構成を組んでみる(mod_auth_openidc編)」を利用したほうがよいかもしれません。
参考資料
- Keycloak Documentation
- 1.2. Supported Platforms
- 2.1.1. Java Adapter Config
- 2.1.5. Tomcat 6, 7 and 8 Adapters
- NRI OpenStandia Keycloak日本語ドキュメント
脚注
-
Keycloakサーバーとのssl接続で自己署名サーバー証明書を利用する場合にはクライアント・アダプター側のJavaのtruststoreにサーバー証明書の取り込みが必要です。(クライアント・アダプターが、Keycloakサーバーと直接SSL通信するため) ↩