Tech Sketch Bucket of Technical Chips by TIS Inc.

OpenAMによるシングルサインオン(1)エージェント編

Pocket

社内に多数のWebアプリケーションが乱立し、それぞれが異なったユーザー認証を採用しているため、ユーザーの利便性が損なわれていることがあります(サイト毎のユーザーID・パスワードを覚えなければならない、異なったサイトに行くたびにユーザー認証が必要)。これを解決するために、ユーザー認証を一元化する手段としてオープンソースのシングルサインオンのプロダクトOpenAMを用い、既存のWebアプリケーションをどのような手順でシングルサインオン化できるかを2回にわたって紹介します。
今回はOpenAMのエージェントを用い、Webアプリケーションのソースコードを改変する手法を見ていきます。


OpenAMとは

OpenAM は、旧サン・マイクロシステムズ社のOpenSSOの流れをくむWebアプリケーションのシングルサインオンを実現するための認証基盤で、現在はForgeRock社において開発され、CDDL (Common Development and Distribution License)ライセンスのもとで使用できます。
OpenAMによるシングルサインオンは次のような形式がとれます。

openam_arch.jpg

それぞれの特徴を簡単に示します。

1)エージェントの使用
JavaアプリケーションサーバーにOpenAMのエージェントをインストールします。エージェントはServletFilterとして機能し、OpenAMに登録されているWebアプリケーションのユーザー認証が必要なページの情報を元に、ユーザー認証必要時にOpenAMでユーザー認証処理を行います。ユーザー認証済みの情報はクッキーiPlanetDirectoryProに格納され、他のOpenAMでユーザー認証を行うWebアプリケーションにアクセスする際はこのクッキーによりユーザー認証済みと判断されることでシングルサインオンが実現されます。

2)リバースプロキシー+エージェントの使用
リバースプロキシーとして OpenIG を用い、そこにOpenAMのエージェントもインストールします。これによりOpenIG使用時はOpenAMでユーザー認証済みの状態になり、シングルサインオンの対象となります。
OpenIGにはWebアプリケーションのユーザー認証が必要なページ・認証時にPOSTする内容を登録し、そのページが呼ばれた場合にOpenAMでのユーザー認証済みの情報をWebアプリケーションにPOSTすることでユーザー認証処理が自動で行われます。

3)RESTの使用
OpenAMはREST(あるいはWebサービス)でのユーザー認証(ユーザー名とパスワードの妥当性判断)が可能です。ただし、エージェントの場合とは異なり、ユーザー認証済みの情報が自動でクッキーに格納されることはありませんので、シングルサインオンを実現するには何らかの工夫が必要です。

4)SAMLの使用
OpenAMはSAMLのIdPとして機能しますので、SAMLのSPの機能を持ったWebアプリケーションをOpenAMでユーザー認証することが可能で、エージェントの場合と同様にクッキーiPlanetDirectoryProが用いられることでシングルサインオンが実現されます。
なお、SAMLの概要はIPAによる解説記事の アイデンティティ管理技術解説 を参照ください。

この4つの形式のうち、"1)エージェント"、"2)リバースプロキシー+エージェント"を用い、Springのプロジェクトで公開されているWebアプリケーションjPetStoreを対象に、シングルサインオン化の手順を紹介します。

jPetStoreでのユーザー認証処理

シングルサインオンを実現するに際しては、jPetStoreが内部的にどのようにユーザー認証を実現しているかを理解する必要がありますので、その処理を紹介します。

ソースコードの入手と稼働まで

jPetStoreのソースコードはSpringのSubversionから次のコマンドで入手します。

WARファイルの作成はmvnコマンドで行えますがpom.xmlファイルの修正が必要です。
使用するSpringのバージョン指定が現在は提供されていない"3.0.0.BUILD-SNAPSHOT"となっていますので、3.0の系列の最終版の"3.0.7.RELEASE"に変更します。
また、repositoryの指定で"JBoss repository"が指定されていますが、ライブラリの依存関係の解釈が失敗しますので削除します。
これにより、"mvn package"でWARファイルが作成されますが、アプリケーションサーバーにデプロイして稼働させる際に slf4jのライブラリ が足りないため、slf4jのライブラリを入手し、その中のslf4j-jdk14-1.5.8.jarをWEB-INF/libに追加します。
また、稼働に際してはRDBMSが必要ですが、今回は簡単のためhsqldbを使用します。入手したjPetStoreのソースコード中のディレクトリdb/hsqldbでserver.shで起動します。

ユーザー認証時の画面遷移の確認

jPetStoreでのユーザー認証時の画面遷移とその際のURLを示します。

jpet_org_flow.jpg

ユーザー認証が必要なページ

ユーザー認証が必要なページは、src/main/webapp/WEB-INF/petstore-servlet.xmlの中のsecureHandlerMappingの欄で定義されます。

この中のurlMapで列挙された/shop/editAccount.do、/shop/listOrders.do、/shop/newOrder.do、/shop/viewOrder.doがユーザー認証が必要なページになります。

ユーザー認証のロジック

ユーザー認証は上述のpetstore-servlet.xmlでinterceptorsがsignonInterceptorなので、org.springframework.samples.jpetstore.web.spring.SignonInterceptorでユーザー認証済みかの判断を行います。コードを抜粋します

ここではセッションオブジェクトにuserSessionがあればユーザー認証済みなので何もしないで本来の処理をそのまま実行します。userSessionが無ければユーザー認証済みでないと判断し、ユーザー認証に移ります。このためにSignonForm(webapp/WEB-INF/jsp/spring/SignonForm.jsp)にリダイレクトし、ユーザー名/パスワードの入力を促します。
SignonFormで入力されたデータは/shop/signon.doで処理されます。これはpetstore-servlet.xmlで

と定義されているので、org.springframework.samples.jpetstore.web.spring.SignonControllerで、ユーザー名・パスワードの妥当性判断を行います。コードを抜粋します

ここではPOSTされたユーザー名・パスワードをRDBMSで検索し、見つかればユーザー認証が成功したとしてユーザーの属性情報をセッションオブジェクトに登録し、元のページにリダイレクトします。

ユーザー認証時のSQL

RDBMSの検索処理はsrc/main/java/org/springframework/samples/jpetstore/dao/ibatis/SqlMapAccountDao.javaのメソッドgetAccountで

が呼び出されるので、実行されるSQLはsrc/main/java/org/springframework/samples/jpetstore/dao/ibatis/maps/Account.xmlのgetAccountByUsernameAndPasswordの箇所

になります。このSQLではユーザー名・平文のパスワードが入っているsignonテーブルで入力されたユーザー名・パスワードの妥当性をチェックを行うだけではなく、account,profileテーブルからユーザーの属性を取得することを同時に行っています。

OpenAMの準備

OpenAMのインストール

OpenAM (WARファイルです)はJavaアプリケーションサーバー上で稼働します。今回は Tomcat7.0.39 を用います。
OpenAMのWARファイルは ForgeRock社のサイト から入手できます。今回使用したバージョンはopenam-server-10.2.0-SNAPSHOT_20130311.warです。
このWARファイルをTomcatにデプロイし、環境設定(LDAPの指定など)を行います。手順はマニュアルの OpenAM 10.2.0 Installation Guide を参照ください。
その後、OpenAMのコンソールでjPetStoreが稼働するアプリケーションサーバーをエージェントとして登録します。手順はマニュアルの 6.6. Configuring J2EE Policy Agents を参照ください。

エージェントのインストール

jPetStoreはOpenAMとは別インスタンスのTomcat上で稼働させ、ここにOpenAMのエージェントをインストールします。
OpenAMのエージェントは ForgeRock社のサイト の"Java EE Policy Agents"欄のTomcatを入手します。今回使用したバージョンはBuild Dateが20130310です。
インストール手順はマニュアルの Chapter 9. Installing the Apache Tomcat Policy Agent を参照ください。

ユーザー認証データの移行

jPetStoreでのユーザー認証に関する情報をOpenAMに移行する必要があります。
jPetStoreではRDBMS上のsignonテーブルでユーザー名・パスワードを保持しますので、それをOpenAMが使用するLDAPに移行します(OpenAMのコンソールの"対象"でユーザー名・パスワードを登録します)。
なお、注意点として、今回のjPetStoreは簡単なサンプルのためパスワードが平文なのでそのままLDAPにデータ移行が可能ですが、通常はパスワードは暗号化して保持されるはずなのでそのままではLDAPには登録できず、初期パスワードの再発行といった移行方針を検討することになります。

OpenAMの設定

ユーザー認証が必要なページの登録

jPetStoreのユーザー認証が必要なページの登録は、OpenAMのコンソールのエージェントの設定画面の"アプリケーション"のタブで行います。注意点としてデフォルトでは"適用されないURIの反転"にはチェックがつかず認証が不要なページを登録しますので、今回はチェックをつけ認証が必要なページだけを登録します。

openam_console1.png

ログアウトのページの登録

jPetStoreではsignoff.doが呼ばれたらログアウトとします。
これはエージェントの設定画面の"アプリケーション"のタブで行います。

openam_console2.png

エージェントのデフォルト設定の変更

エージェントフィルタモードをSSO_ONLYに、またOpenAMでのユーザー認証時にjPetStoreのHTTPセッションが破棄されますので(jPetStoreのショッピングカートが消去される)、HTTPセッションバインドのチェックを外しそれを無効にします。
これはエージェントの設定画面の"グローバル"のタブで行います。

openam_console3.png

jPetStoreのソースコードの改修

ログイン済みかの確認処理SignonInterceptorの修正

エージェントを用いることにより、ログイン済みかの確認処理SignonInterceptorが呼ばれた時にはOpenAMで既に認証済みであり、未認証時のログインページへのリダイレクト処理は不要となりますので削除します。
ただし認証はされていますが、誰がユーザー認証されたかは不明です。そこで、OpenAMのクッキーiPlanetDirectoryProより、RESTインターフェースでOpenAMからユーザー属性を取得する処理を追加します。
RESTのurlは"/openam/identity/attributes?subjectid=クッキーiPlanetDirectoryProの値"で、このレスポンスデータの

  • userdetails.attribute.name=uid
  • userdetails.attribute.value= ログインしたユーザー名

という項目よりログインしたユーザー名を取得します。このコードは次のようになります。

呼ばれなくなる認証処理SigonControllerの取り扱い

認証処理SigonControllerで行われていたユーザー情報のセッションへの格納処理をSignonInterceptorに移す必要があります(上記のユーザー名の取得後に)。

注:getAccountのRDBMS検索でsignonテーブルを対象とするのは不要になり(パスワードの妥当性チェックを行う必要がなくなるので)、SQL文のチューニングの余地がありますが、今回は簡単のためSQL文の変更はせず、ユーザー名とパスワードが同じ前提で処理を行います。

また、petstore-servlet.xmlのsignonInterceptorの欄にRDBMSアクセスで使用するための変数PetStoreFacadeの設定を追加します。

稼働確認

今回シングルサインオン化したjPetStoreのユーザー認証時の画面遷移を示します。

jpet_sso_flow.jpg

ユーザー認証はOpenAMのサーバーにリダイレクトされることで行われるようになりました。他のOpenAMでユーザー認証を行うWebアプリケーション使用後にこのjPetStoreを使用すると、OpenAMでの認証画面はスキップされ、シングルサインオン化されています。

最後に

OpenAMのエージェントを用いることにより、独自のユーザー認証を行っていたWebアプリケーションjPetStoreを、

  • OpenAMに認証が必要なページを登録
  • 独自の認証処理の削除
  • セッションオブジェクトにユーザー情報を設定する箇所の変更

という簡単な修正処理で、シングルサインオン化できることを紹介しました。

次回は、ソースの修正を行わずにシングルサインオン対応する手段として、リバースプロキシーの使用を紹介します。

エンジニア採用中!私たちと一緒に働いてみませんか?