3.4. サブジェクトの認証

サブジェクトの認証には JAAS ログインが必要です。ログイン手順は次のようになります。
  1. アプリケーションは LoginContext をインスタンス化し、ログイン設定の名前と CallbackHandler を渡して、設定 LoginModule が必要とする Callback オブジェクトを追加します。
  2. LoginContext は、名前付きログイン設定に含まれるすべての LoginModules をロードするため Configuration を確認します。このような名前付き設定が存在しない場合は、other 設定がデフォルトで使用されます。
  3. アプリケーションによって、LoginContext.login メソッドが呼び出されます。
  4. ログインメソッドはロードされたすべての LoginModule を呼び出します。各 LoginModule はサブジェクトを認証するため、関連する LoginModule でハンドルメソッドを呼び出し、認証プロセスに必要な情報を取得します。必要な情報は、Callback オブジェクトのアレイの形式でハンドルメソッドに渡されます。認証に成功すると、LoginModule は関連のプリンシパルとクレデンシャルをサブジェクトに関連付けします。
  5. LoginContext は認証ステータスをアプリケーションに返します。ログインメソッドから返されると認証が成功したことになります。ログインメソッドによって LoginException がスローされると認証に失敗したことになります。
  6. 認証に成功すると、アプリケーションは LoginContext.getSubject メソッドを使用して認証されたサブジェクトを取得します。
  7. サブジェクトの認証が完了した後に LoginContext.logout メソッドを呼び出すと、login メソッドによりサブジェクトに関連付けられたすべてのプリンシパルおよび関連情報を削除できます。
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 をチェーン化し、複数の認証技術を認証プロセスに加えることが可能です。たとえば、1 つの LoginModule がユーザー名およびパスワードベースの認証を行い、別の LoginModule をスマートカードリーダや生体認証などのハードウェアデバイスへ接続するインターフェースとすることが可能です。
LoginModule のライフサイクルは、クライアントがログインメソッドを作成し公開するLoginContext オブジェクトによって決定されます。このプロセスには 2 つのフェーズがあり、プロセスの手順は次のようになります。
  • LoginContext は引数のないパブリックコンストラクターを使用して、設定された LoginModule を作成します。
  • LoginModule は、初期化メソッドへの呼び出しによって初期化されます。Subject 引数は null 以外になることが保証されます。初期化メソッドのシグネチャーは 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 の全体的な認証が成功すると、各 LoginModulecommit が呼び出されます。フェーズ 1 が LoginModule に対して成功すると、コミットメソッドはフェーズ 2 を続行し、関連するプリンシパル、パブリッククレデンシャル、プライベートクレデンシャルをサブジェクトに関連付けます。フェーズ 1 が LoginModule に対して失敗すると、commit はユーザー名やパスワードなどの以前保存した認証状態をすべて削除します。commit メソッドのシグネチャーは boolean commit() throws LoginException です。LoginException がスローされると、コミットフェーズの完了に失敗したことを示します。true が返されるとメソッドが成功したことを示し、false が返されるとログインモジュールが無視されることを示します。
  • LoginContext の全体的な認証が失敗すると、各 LoginModuleabort メソッドが呼び出されます。abort メソッドはログインまたは初期化メソッドによって作成されたすべての認証状態を削除または破棄します。abort メソッドのシグネチャーは boolean abort() throws LoginException です。LoginException がスローされると abort フェーズの完了に失敗したことを示します。true が返されるとメソッドが成功したことを示し、false が返されるとログインモジュールが無視されることを示します
  • ログイン成功後に認証状態を削除するため、アプリケーションは LoginContextlogout を呼び出します。これにより、各 LoginModulelogout メソッドが呼び出されます。logout メソッドは、commit 操作中に当初サブジェクトに関連付けられていたプリンシパルとクレデンシャルを削除します。クレデンシャルは削除時に破棄されるはずです。logout メソッドのシグネチャーは boolean logout() throws LoginException です。LoginException がスローされるとログアウトプロセスの完了に失敗したことを示します。true が返されるとメソッドが成功したことを示し、false が返されるとログインモジュールが無視されることを示します。
LoginModule がユーザーと通信して認証情報を取得する必要がある場合、CallbackHandler オブジェクトを使用します。アプリケーションは、 CallbackHandler インターフェースを実装して LoginContext に渡し、基礎となるログインモジュールに直接認証情報を送信します。
ログインモジュールは、CallbackHandler を使用して、パスワードやスマートカード PIN などのユーザー入力による情報を取得したり、ステータスなどの情報をユーザーに提供したりします。アプリケーションによる CallbackHandler の指定を可能にすることで、基礎となる LoginModule がアプリケーションとユーザーが対話するさまざまな方法に依存しないようにします。たとえば、GUI アプリケーションの CallbackHandler の実装は、ウィンドウを表示してユーザーの入力を求めることがあります。一方でアプリケーションサーバーなどの GUI でない環境の CallbackHandler 実装は、アプリケーションサーバー API を使用してクレデンシャル情報を取得することがあります。 CallbackHandler インターフェースには実装するメソッドが 1 つあります。
void handle(Callback[] callbacks)
    throws java.io.IOException, 
           UnsupportedCallbackException;
最後に説明する認証クラスは Callback インターフェースです。これは複数のデフォルト実装が提供されているタグ付けインターフェースで、前述の例で使用した NameCallbackPasswordCallback が含まれます。LoginModuleCallback を使用し、認証メカニズムで必要となる情報を要求します。LoginModule は認証のログインフェーズの間に Callback のアレイを直接 CallbackHandler.handle メソッドに渡します。callbackhandler がハンドルメソッドに渡された Callback オブジェクトの使用方法が分からない場合は、UnsupportedCallbackException をスローしてログイン呼び出しを中止します。