第2章 JAAS の概略
JBossSX フレームワークは JAAS API を基にしています。JBossSX の実装について詳しく理解するためには、JAAS API の基本要素を理解する必要があります。JBossSX アーキテクチャについては本書の後半で説明するため、次項では JAAS について少し説明しておきます。
JAAS 1.0 API はユーザー認証と承認を目的としてつくられた Java パッケージのセットで構成されています。API は標準の PAM (Pluggable Authentication Modules : プラグ可能な認証モジュール) フレームワークの Java バージョンを実装し、ユーザーベースの承認に対応するよう Java 2 Platform のアクセス制御アーキテクチャを拡張します。
JAAS は最初 JDK 1.3 の拡張パッケージとしてリリースされ、JDK 1.5 に同梱されています。JBossSX フレームワークは JAAS の認証機能のみを使用して、宣言型ロールベースの J2EE セキュリティモデルを実装するため、本項ではこの部分にのみ焦点を置いて説明します。
JAAS 認証はプラグ可能な方法で実行されます。これにより Java アプリケーションが基礎となる認証技術に依存することなく、JBossSX セキュリティマネージャが異なるセキュリティインフラストラクチャで動作することが可能になります。セキュリティインフラストラクチャとの統合は JBossSX セキュリティマネージャの実装を変更することなく実現できます。変更が必要なのは、JAAS が使用する認証スタックの設定のみです。
2.1. JAAS コアクラス
JAAS コアクラスは共通、認証、承認の 3 つのカテゴリに区分することができます。本項で取り上げる JBossSX の機能性を実装するために使用されるクラスは共通と認証のクラスであるため、以下の一覧ではそれら 2 つのみ記載します。
共通のクラスは次の通りです。
Subject
(javax.security.auth.Subject
)Principal
(java.security.Principal
)
認証のクラスは次の通りです。
Callback
(javax.security.auth.callback.Callback
)CallbackHandler
(javax.security.auth.callback.CallbackHandler
)Configuration
(javax.security.auth.login.Configuration
)LoginContext
(javax.security.auth.login.LoginContext
)LoginModule
(javax.security.auth.spi.LoginModule
)
2.1.1. サブジェクトとプリンシパルクラス
リソースへのアクセスを承認するには、アプリケーションは最初に要求元を認証する必要があります。JAAS フレームワークは要求元を表すための用語サブジェクトを定義します。
Subject
のクラスは JAAS の中心クラスです。Subject
は人やサービスなど単一のエンティティの情報を表します。情報はエンティティのプリンシパル、パブリックの資格情報、プライベートの資格情報などに渡ります。JAAS API は既存の Java 2 java.security.Principal
インターフェースを使用して、プリンシパルを表します。プリンシパルは基本的に入力された名前になります。
認証プロセスでは、サブジェクトには関連付けられたアイデンティティまたはプリンシパルが含まれています。サブジェクトは多くのプリンシパルを持つことができます。例えば、一個人は名前のプリンシパル (John Doe)、ソーシャルセキュリティ番号のプリンシパル (123-45-6789)、ユーザー名のプリンシパル (johnd) を持つことができ、これらすべては他のサブジェクトから区別するのに役立ちます。1 つのサブジェクトに関連付けられたプリンシパルを取得する方法は 2 つあります。
public Set getPrincipals() {...} public Set getPrincipals(Class c) {...}
getPrincipals()
はサブジェクトに含まれるすべてのプリンシパルを返します。getPrincipals(Class c)
はクラス c
のインスタンスまたはそのサブクラスのひとつであるプリンシパルのみを返します。サブジェクトに一致するプリンシパルがない場合は空のセットが返されます。
java.security.acl.Group
インターフェースは java.security.Principal
のサブインターフェースであるため、プリンシパルのセットのインスタンスは他のプリンシパルやプリンシパルのグループの論理グループを表します。
2.1.2. サブジェクトの認証
サブジェクトの認証には JAAS ログインが必要です。ログイン手順は次のようになります。
- アプリケーションは
LoginContext
のインスタンスを作成し、ログイン設定の名前とCallbackHandler
を渡して、設定LoginModule
で必要とされるとおりCallback
オブジェクトを追加します。 LoginContext
は、名前付きログイン設定に含まれているすべてのLoginModules
をロードするようConfiguration
と確認します。そうした名前付き設定が存在しない場合は、other
設定がデフォルトとして使用されます。- アプリケーションが
LoginContext.login
メソッドを呼び出します。 - ログインメソッドはロードされたすべての
LoginModule
を呼び出します。各LoginModule
はサブジェクトの認証を試行するため、関連付けられたCallbackHandler
でハンドルメソッドを呼び出し、認証プロセスに必要な情報を取得します。必要な情報はCallback
オブジェクトのアレイの形式でハンドルメソッドに渡されます。成功すると、LoginModule
は関連のプリンシパルと資格情報をサブジェクトに関連付けします。 LoginContext
はアプリケーションに認証状態を返します。ログインメソッドからの返されると成功となります。ログインメソッドによって LoginException がスローされると失敗となります。- 認証が成功したら、アプリケーションは
LoginContext.getSubject
メソッドを使用して認証されたサブジェクトを取得します。 - サブジェクトの認証が完了した後に
LoginContext.logout
メソッドを呼び出すと、ログインメソッドによりサブジェクトに関連付けられたすべてのプリンシパルと関連情報を削除することができます。
LoginContext
クラスは認証しているサブジェクトに基本メソッドを提供し、基礎となる認証技術に依存しないアプリケーションを開発する方法を提供します。LoginContext
は特定のアプリケーション向けに設定された認証サービスを決定するよう Configuration
と確認します。LoginModule
クラスは認証サービスを表します。そのため、アプリケーション自体を変更することなくログインモジュールをアプリケーションにプラグインすることが可能です。次のコードは、サブジェクトを認証するためアプリケーションに必要となるステップを示しています。
CallbackHandler handler = new MyHandler(); LoginContext lc = new LoginContext("some-config", handler); try { lc.login(); Subject subject = lc.getSubject(); } catch(LoginException e) { System.out.println("authentication failed"); e.printStackTrace(); } // Perform work as authenticated Subject // ... // Scope of work complete, logout to remove authentication info try { lc.logout(); } catch(LoginException e) { System.out.println("logout failed"); e.printStackTrace(); } // A sample MyHandler class class MyHandler implements CallbackHandler { public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { for (int i = 0; i < callbacks.length; i++) { if (callbacks[i] instanceof NameCallback) { NameCallback nc = (NameCallback)callbacks[i]; nc.setName(username); } else if (callbacks[i] instanceof PasswordCallback) { PasswordCallback pc = (PasswordCallback)callbacks[i]; pc.setPassword(password); } else { throw new UnsupportedCallbackException(callbacks[i], "Unrecognized Callback"); } } } }
開発者は
LoginModule
インターフェースの実装を作成することで、認証技術を統合します。これにより管理者は異なる認証技術を 1 つのアプリケーションにプラグインできます。複数の LoginModule
をチェーン化し複数の認証技術を認証プロセスに加えることが可能です。例えば、ある LoginModule
がユーザー名 / パスワードベースの認証を行い、別の LoginModule
はスマートカードリーダや生体認証などのハードウェアデバイスに接続することができます。
LoginModule
のライフサイクルは、クライアントがログインメソッドを作成し公開するLoginContext
オブジェクトによって決定されます。このプロセスには 2 つのフェーズがあり、プロセスの手順は次のようになります。
LoginContext
は引数のないパブリックコンストラクタを使用して、設定されたLoginModule
を作成します。- 各
LoginModule
は initialize メソッドへの呼び出しによって初期化されます。Subject
引数は null 以外になることが保証されます。initialize メソッドのシグネチャはpublic void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options)
です。 login
メソッドは認証プロセスを開始するために呼び出されます。例えば、あるメソッド実装はユーザーにユーザー名とパスワードの入力を求め、NIS または LDAP などのネーミングサービスで保存されているデータに対してこの情報を確認することがあります。別の実装ではスマートカードや生体認証デバイスに接続されるか、単に基礎となるオペレーティングシステムからユーザー情報を抽出することがあります。各LoginModule
によるユーザーアイデンティティの検証は JAAS 認証のフェーズ 1 とみなされます。login
メソッドのシグネチャはboolean login() throws LoginException
です。LoginException
は失敗を意味します。true の戻り値はメソッドが成功したことを示し、false の戻り値はログインモジュールが無視されることを示しています。LoginContext
の全体的な認証が成功すると、各LoginModule
でcommit
が呼び出されます。フェーズ 1 がLoginModule
に対し成功すると、コミットメソッドではフェーズ 2 が続き、関連するプリンシパル、パブリックの資格情報、プライベートの資格情報をサブジェクトに関連付けます。フェーズ 1 がLoginModule
に対し失敗すると、commit
はユーザー名やパスワードなど以前に保存していた認証状態をすべて削除します。commit
メソッドのシグネチャはboolean commit() throws LoginException
です。LoginException
がスローされると、コミットフェーズの完了が失敗したことを示します。true が返されるとメソッドが成功したことを示し、false が返されるとログインモジュールが無視されることを示します。LoginContext
の全体的な認証が失敗すると、各LoginModule
でabort
メソッドが呼び出されます。abort
メソッドはログインまたは initialize メソッドによって作成されたすべての認証状態を削除または破棄します。abort
メソッドのシグネチャはboolean abort() throws LoginException
です。LoginException
がスローされるとabort
フェーズの完了が失敗したことを示します。true が返されるとメソッドが成功したことを示し、false が返されるとログインモジュールが無視されることを示します。- ログイン成功後に認証状態を削除するには、アプリケーションは
LoginContext
でlogout
を呼び出します。これにより、各LoginModule
でlogout
メソッド呼び出しが発生します。logout
メソッドはcommit
動作時に当初サブジェクトに関連付けられていたプリンシパルと資格情報を削除します。資格情報は削除時に破棄されるべきです。logout
メソッドのシグネチャはboolean logout() throws LoginException
です。LoginException
がスローされるとログアウトプロセスの完了が失敗したことを示します。true が返されるとメソッドが成功したことを示し、false が返されるとログインモジュールが無視されることを示します。
LoginModule
が認証情報を取得するためユーザーと交信する必要がある場合、CallbackHandler
オブジェクトを使用します。アプリケーションは CallbackHandler インターフェースを実装してそれを LoginContext
に渡し、直接基礎となるログインモジュールに認証情報を送ります。
ログインモジュールは、パスワードやスマートカード PIN などのユーザーからの入力を収集し、状態情報などをユーザーに提供するために
CallbackHandler
を使用します。アプリケーションに CallbackHandler
を指定できるようにすることで、基礎となる LoginModule
はアプリケーションがユーザーと対話する様々な方法に依存しない状態を維持します。例えば GUI アプリケーションの CallbackHandler
の実装は、ウィンドウを表示してユーザーの入力を求めることがあります。 一方でアプリケーションサーバーなど GUI でない環境の CallbackHandler
実装は、アプリケーションサーバー API を使用して単に資格情報を取得することがあります。CallbackHandler インターフェースには実装するメソッドが 1 つあります。
void handle(Callback[] callbacks) throws java.io.IOException, UnsupportedCallbackException;
最後に説明する認証クラスは
Callback
インターフェースです。これは複数のデフォルト実装が提供されているタグ付けインターフェースで、前述の例で使用した NameCallback
と PasswordCallback
が含まれます。LoginModule
は Callback
を使用し、認証メカニズムで必要となる情報を要求します。LoginModule
は認証のログインフェーズの間に Callback
のアレイを直接 CallbackHandler.handle
メソッドに渡します。callbackhandler
がハンドルメソッドに渡された Callback
オブジェクトの使用方法が分からない場合は、UnsupportedCallbackException
をスローしてログイン呼び出しを中止します。