WebLogic Security サービスの開発
認証とは、呼び出し側が、特定のユーザまたはシステムの代わりに動作していることを証明する際に使用するメカニズムのことです。認証は、ユーザ名とパスワードの組み合わせなどの資格を使用して「あなたは誰」という問いに答えます。
WebLogic Server では、ユーザまたはシステム プロセスの ID を証明するために認証プロバイダを使用します。認証プロバイダでは、ID 情報を記憶したり、転送したり、その情報が必要な場合にサブジェクトを通じてシステムのさまざまなコンポーネントで利用できるようにしたりします。認証プロセスでは、プリンシパル検証プロバイダが、サブジェクト内に格納されるプリンシパル (ユーザおよびグループ) のセキュリティを強化するため、それらのプリンシパルに署名して信頼性を検証します (詳細については、「プリンシパル検証プロバイダ」を参照してください)。
以下の節では、認証プロバイダの概念と機能、およびカスタム認証プロバイダの開発手順について説明します。
注意 : ID アサーション プロバイダは、ユーザまたはシステム プロセスがトークンを使用してそれぞれの ID を証明する特殊な形態の認証プロバイダです。詳細については、「ID アサーション プロバイダ」を参照してください。
カスタム認証プロバイダの開発の詳細に立ち入る前に、以下の概念を理解しておくことが大切です。
ユーザは、人を表すという点でオペレーティング システムのユーザに似ています。一方、グループはユーザのカテゴリで、肩書きなどの共通の特徴で分類されたものです。ユーザをグループに分類すると、多数のユーザのアクセス パーミッションを管理しやすくなります。ユーザとグループの詳細については、『WebLogic リソースのセキュリティ』の「ユーザとグループ」を参照してください。
WebLogic Server のようなアプリケーション サーバでは、ユーザもグループもプリンシパルとして使用することができます。プリンシパルは、認証の結果としてユーザまたはグループに割り当てられる ID です。JAAS (Java Authentication and Authorization Service) では、プリンシパルなどの認証情報のコンテナとしてサブジェクトを使用することになっています。同じサブジェクト内に格納されている各プリンシパルは、ある人の財布に入っている複数のカードのように、同じユーザの ID を異なる観点で表しています。たとえば、持ち主の身元を銀行に示すために ATM カードがあり、所属する組織に示すためにメンバシップ カードがある、という具合です。JAAS の詳細については、「JAAS (Java Authentication and Authorization Service)」を参照してください。
注意 : サブジェクトは、WebLogic Server 6.x のユーザに取って代わるものです。
図 3-1 に、ユーザ、グループ、プリンシパル、およびサブジェクト間の関係を示します。
図 3-1 ユーザ、グループ、プリンシパル、およびサブジェクトの間の関係
認証に成功すると、その一環として、プリンシパルは署名され、その後の使用に備えてサブジェクトに格納されます。プリンシパルに署名するのはプリンシパル検証プロバイダであり、実際にプリンシパルをサブジェクトに格納するのは LoginModule です。後で、サブジェクト内に格納されたプリンシパルに呼び出し側がアクセスしようとすると、そのプリンシパルが署名されてから変更されていないことをプリンシパル検証プロバイダが確認したうえで、そのプリンシパルが呼び出し側に返されます (それ以外のセキュリティ条件がすべて満たされている場合)。
注意 : プリンシパル検証プロバイダと LoginModule の詳細については、それぞれ「プリンシパル検証プロバイダ」と「LoginModule」を参照してください。
WebLogic Server ユーザまたはグループを表すプリンシパルは、weblogic.security.spi
パッケージの WLSUser
インタフェースと WLSGroup
インタフェースを実装する必要があります。
LoginModule は、認証プロバイダの必須コンポーネントであり、境界認証用に別個の LoginModule を開発する必要がある場合は ID アサーション プロバイダのコンポーネントにもなります。
LoginModule は、認証処理における「馬車馬」のような存在です。すべての LoginModule は、セキュリティ レルム内のユーザの認証と、サブジェクト内への必要なプリンシパル (ユーザ/グループ) の格納を担当します。境界認証に使用されない LoginModule も、提示された証明情報 (たとえば、ユーザのパスワード) が正しいかどうかを確認します。
注意 : ID アサーション プロバイダと境界認証の詳細については、「ID アサーション プロバイダ」を参照してください。
セキュリティ レルムに複数の認証プロバイダがコンフィグレーションされている場合、各認証プロバイダの LoginModule は同じサブジェクトにプリンシパルを格納します。したがって、ある認証プロバイダの LoginModule によって、WebLogic Server ユーザ (WLSUser
インタフェースの実装) を表すプリンシパル「Joe」が追加された場合、セキュリティ レルム内の他のすべての認証プロバイダは、「Joe」に出会ったときに同じ人物を参照する必要があります。つまり、他の認証プロバイダの LoginModule は、同じ人物を参照するために、WebLogic Server ユーザを表す別のプリンシパル (「Joseph」など) をサブジェクトに追加しようとしてはなりません。ただし、別の認証プロバイダの LoginModule は、WLSUser
以外のタイプのプリンシパルを「Joseph」という名前で追加することができます。
LoginModule は、ユーザ名とパスワードの組み合わせ、スマート カード、バイオメトリック装置などのさまざまな認証メカニズムを扱うように作成することができます。LoginModule を開発するには、JAAS (Java Authentication and Authorization Service) に基づき、認証情報のコンテナとしてサブジェクトを使用する javax.security.auth.spi.LoginModule
インタフェースを実装します。LoginModule
インタフェースを使用すると、単一アプリケーション用にさまざまな種類の認証技術をプラグインすることができ、WebLogic Security フレームワークはさまざまな要素から成る認証用に複数の LoginModule
実装をサポートするように設計されています。さらに、LoginModule インスタンス間に依存関係を設けたり、それらのインスタンス間で資格を共有することもできます。ただし、LoginModule と認証プロバイダの関係は 1 対 1 です。つまり、網膜スキャン認証を扱う LoginModule と、スマート カードのようなハードウェア デバイスとのインタフェースを取る LoginModule を用意するには、それぞれに LoginModule
インタフェースの実装が含まれる 2 つの認証プロバイダを開発およびコンフィグレーションする必要があります。詳細については、「JAAS LoginModule インタフェースを実装する」を参照してください。
注意 : LoginModule は、独自に開発するのではなくサードパーティのセキュリティ ベンダから入手することもできます。
複数の認証プロバイダ (その結果として複数の LoginModule) をコンフィグレーションする方法は、認証プロセスの全体的な結果に影響します。これは、さまざまな要素から成る認証で特に重要です。まず、LoginModule は認証プロバイダのコンポーネントであるため、認証プロバイダがコンフィグレーションされた順序で呼び出されます。通常、認証プロバイダのコンフィグレーションには、WebLogic Server Administration Console を使用します (詳細については、「認証プロバイダの順序を指定する」を参照してください)。 次に、各 LoginModule の制御フラグがどのように設定されるかによって、認証プロセスでのエラーの処理方法が決まります。図 3-2 は、それぞれ異なる認証プロバイダに属する 3 つの LoginModule が関わるサンプル フローを示すとともに、異なる認証結果でサブジェクトに何が起こるのかを示しています。
カスタム認証プロバイダ #1 の制御フラグが REQUIRED に設定されていた場合、ユーザ認証ステップで認証が失敗すると、認証プロセス全体が失敗します。また、ユーザが WebLogic 認証プロバイダ (またはカスタム認証プロバイダ #2) によって認証されなかった場合、認証プロセス全体が失敗します。認証プロセスがこのように失敗した場合、3 つの LoginModule すべてがロールバックされ、サブジェクトにプリンシパルが格納されません。
注意 : LoginModule 制御フラグの設定と LoginModule インタフェースの詳細については、それぞれ『Java Authentication and Authorization Service (JAAS) 1.0 LoginModule Developer's Guide』および Java 2 Enterprise Edition, v1.4.1 API 仕様 Javadoc の「LoginModule インタフェース」を参照してください。
クライアントが認証の必要なアプリケーション、アプレット、エンタープライズ JavaBean (EJB)、あるいはサーブレットのいずれであろうと、WebLogic Server では、JAAS (Java Authentication and Authorization Service) クラスを用いて、信頼性とセキュリティを確保しつつクライアントに対する認証を行います。JAAS では PAM (プラグイン可能な認証モジュール) フレームワークの Java 版を実装しており、それを使用することでアプリケーションは基礎となる認証技術からの独立性を保つことができるようになります。このため、PAM フレームワークを利用することで、アプリケーションに修正を加えることなく新しいまたは最新版の認証技術を使用することができます。
WebLogic Server は、リモートのファット クライアントの認証および内部の認証で JAAS を使用します。したがって、JAAS に直に関与する必要があるのは、カスタム認証プロバイダの開発者とファット クライアント アプリケーションの開発者だけです。シン クライアントのユーザまたはコンテナ内のファット クライアント アプリケーション (サーブレットからエンタープライズ JavaBeans を呼び出すものなど) の開発者は、JAAS を直接使用したり、その知識を身につけたりする必要はありません。
通常、JAAS クラスと WebLogic Server フレームワークを用いた認証は以下のように実行されます。
LoginContext
クラスを使用して CallbackHandler
をローカル (クライアントサイド) LoginModule に渡します。ローカル LoginModule は WebLogic Server の一部として提供されている UsernamePasswordLoginModule
の場合があります。 CallbackHandler
を適切な WebLogic Server コンテナ (RMI、EJB、サーブレット、IIOP など) に渡します。注意 : CallbackHandler
は、可変個の引数を複合オブジェクトとしてメソッドに渡すことができるようにする高度に柔軟な JAAS 規格です。CallbackHandler
のタイプは、NameCallback
、PasswordCallback
、および TextInputCallback
の 3 つで、いずれも javax.security.auth.callback
パッケージに収められています。NameCallback
と PasswordCallback
は、それぞれユーザ名とパスワードを返します。TextInputCallback
は、ユーザがログイン フォームの追加フィールド (ユーザ名とパスワードを取得するフィールド以外のフィールド) に入力したデータにアクセスするために使用します。TextInputCallback
はフォームの追加フィールドにつき 1 つ必要で、各 TextInputCallback
のプロンプト文字列はフォームのフィールド名と一致する必要があります。WebLogic Server は、フォームベースの Web アプリケーション ログインに対してだけ TextInputCallback
を使用します。CallbackHandler
の詳細については、Java 2 Enterprise Edition, v1.4.1 API 仕様 Javadoc を参照して CallbackHandler インタフェースを調べてください。
LoginContext
クラスの詳細については、Java 2 Enterprise Edition, v1.4.1 仕様 Javadoc を参照して LoginContext クラスを調べてください。
UsernamePasswordLoginModule
の詳細については、WebLogic Server 8.1 API リファレンス Javadoc の「UsernamePasswordLoginModule クラス」を参照してください。
クライアントサイド LoginModule を使用しない場合、ユーザ名とパスワードを他の方法で (たとえば初期 JNDI ルックアップの一部として) 指定できます。
CallbackHandler
が存在する場合、それが WebLogic Security フレームワークに渡されます。CallbackHandler
が作成されます。これらは WebLogic Security フレームワークによってサーバサイドに作成された内部的な CallbackHandler
であり、クライアントの CallbackHandler
とは関係ありません。注意 : LoginModule の詳細については、「LoginModule」を参照してください。
注意 : サーバ側ですべてが行われる認証の場合、プロセスは手順 3 で始まり、WebLogic Server コンテナは手順 4 の前に weblogic.security.services.authentication.login
メソッドを呼び出します。
スタンドアロンの T3 アプリケーションの場合に JAAS クラスが WebLogic Security フレームワークとどう連携するかを 図 3-3 に示し、その後それについて説明します。
図 3-3 JAAS クラスと WebLogic Server を用いた認証
この例で、JAAS クラスと WebLogic Server フレームワークを用いた認証は以下のように実行されます。
注意 : weblogic.security.auth.login.UsernamePasswordLoginModule
は標準の JAAS javax.security.auth.spi.LoginModule
インタフェースを実装し、クライアント側の API を使用して WebLogic Server インスタンスに対する WebLogic クライアントの認証を行います。LoginModule は、T3 クライアントと IIOP クライアントの両方で使用できます。この LoginModule の呼び出し側では、CallbackHandler
を実装してユーザ名 (NameCallback
)、パスワード (PasswordCallback
)、および URL (URLCallback
) を渡す必要があります。
CallbackHandler
が WebLogic Security フレームワークに渡されます。CallbackHandler
が、コンフィグレーションされた認証プロバイダごとに作成されます。これらは WebLogic Security フレームワークによってサーバサイドに作成された内部的な CallbackHandler
であり、クライアントの CallbackHandler
とは関係ありません。
図 3-4 に、ファットクライアント ログインの認証プロセスの仕組みを示します。JAAS はサーバ上で動作してログインを実行します。シンクライアント ログイン (ブラウザ クライアント) の場合でも、JAAS はサーバ上で動作します。
注意 : JAAS プロセスを直接扱うのは、カスタム認証プロバイダの開発者だけです。クライアント アプリケーションは、JNDI 初期コンテキスト作成または JAAS のいずれかを使用してユーザ名とパスワードを受け渡します。
ユーザがユーザ名とパスワードの組み合わせを使用してシステムにログインしようとすると、WebLogic Server ではそのユーザのユーザ名とパスワードを検証することによって信頼を確立し、JAAS の要件に従って、プリンシパルが格納されたサブジェクトを返します。図 3-4 に示すように、このプロセスでは LoginModule とプリンシパル検証プロバイダを使用する必要があります。これらについては、それぞれ「LoginModule」と「プリンシパル検証プロバイダ」で詳しく解説します。
呼び出し側の ID の確認に成功すると、認証コンテキストが確立され、ID が確認されたユーザまたはシステムに関しては、そのコンテキストを通じて他のエンティティに対する認証を行うことができます。認証コンテキストはまた、アプリケーション コンポーネントに委託することもでき、それによって、そのコンポーネントは別のアプリケーション コンポーネントを呼び出しつつ、元の呼び出し側として動作できるようになります。
WebLogic Server のデフォルト (アクティブ) セキュリティ レルムには、WebLogic 認証プロバイダが含まれます。
注意 : WebLogic 認証プロバイダを WebLogic 認可プロバイダと組み合わせれば、WebLogic Server リリース 6.x で利用できたファイル レルムの機能の代わりになります。
WebLogic 認証プロバイダは、ユーザ名とパスワードの委託認証をサポートし、組み込み LDAP サーバを利用してユーザとグループの情報を格納します。WebLogic 認証プロバイダを使用すると、ユーザとグループ メンバシップを編集、表示、および管理できます。追加の認証タスクを実行する場合は、カスタム認証プロバイダを開発する必要があります。
注意 : X509 証明書または CSIv2 (CORBA Common Secure Interoperability version 2) を使用して境界認証を行うには、カスタム ID アサーション プロバイダを開発する必要があります。詳細については、「ID アサーション プロバイダ」を参照してください。
WebLogic 認証プロバイダが開発者のニーズを満たさない場合、次の手順でカスタム認証プロバイダを開発することができます。
この情報を理解し、設計に関する判断を下したら、次の手順でカスタム認証プロバイダの実行時クラスを作成します。
カスタム認証プロバイダの実行時クラスの作成例については、「例 : サンプル認証プロバイダの実行時クラスの作成」を参照してください。
AuthenticationProvider
SSPI を実装するには、「「Provider」SSPI の目的を理解する」で説明されているメソッドと以下のメソッドの実装を提供する必要があります。
getLoginModuleConfiguration
メソッドは、認証プロバイダの関連付けられた LoginModule に関する情報を取得します。その情報は、AppConfigurationEntry
として返されます。AppConfigurationEntry
は、LoginModule のクラス名、認証プロバイダの関連する MBean を通じて渡された LoginModule の制御フラグ、および他のコンフィグレーション情報を LoginModule に渡すことを可能にする LoginModule のコンフィグレーション オプション マップの格納された JAAS (Java Authentication and Authorization Service) クラスです。
AppConfigurationEntry
クラス (javax.security.auth.login
パッケージ) と LoginModule の制御フラグ オプションの詳細については、Java 2 Enterprise Edition, v1.4.1 API 仕様 Javadoc の「AppConfigurationEntry クラス」および「Configuration クラス」を参照してください。LoginModule の詳細については、「LoginModule」を参照してください。セキュリティ プロバイダと MBean の詳細については、「MBean タイプが必要な理由を理解する」を参照してください。
public AppConfigurationEntry getAssertionModuleConfiguration()
getAssertionModuleConfiguration
メソッドは、ID アサーション プロバイダの関連付けられた LoginModule に関する情報を取得します。その情報は、AppConfigurationEntry
として返されます。AppConfigurationEntry
は、LoginModule のクラス名、ID アサーション プロバイダの関連する MBean を通じて渡された LoginModule の制御フラグ、および他のコンフィグレーション情報を LoginModule に渡すことを可能にする LoginModule のコンフィグレーション オプション マップの格納された JAAS クラスです。
注意 : getAssertionModuleConfiguration
メソッドの実装は null
を返す場合があります (ID アサーション プロバイダが認証プロバイダと同じ LoginModule を使用する場合)。
ID アサーション プロバイダの assertIdentity()
メソッドは、ID アサーションが発生するたびに呼び出されますが、サブジェクトがキャッシュされている場合、LoginModule は呼び出されないことがあります。-Dweblogic.security.identityAssertionTTL
フラグを使うと、この動作を左右する (たとえば、5 分というデフォルト TTL を変更する、またはフラグを 0 に設定してキャッシュを無効にする) ことができます。
トークンが有効なだけでなく、ユーザも引き続き有効であること (ユーザが削除されていないことなど) を確認するのは、ID アサーション プロバイダの役割です。
EJB の <run-as-principal>
要素とカスタム認証プロバイダを併用する場合には、この getAssertionModuleConfiguration()
メソッドを使用します。このメソッドが実行する ID アサーションによって、<run-as-principal>
要素に指定されているプリンシパルが検証されます。
getPrincipalValidator
メソッドは、プリンシパル検証プロバイダの実行時クラス (PrincipalValidator
SSPI 実装) の参照を取得します。WebLogic プリンシパル検証プロバイダは、ほとんどの場合で使用できます。WebLogic プリンシパル検証プロバイダの返し方の例については、コード リスト 3-1 を参照してください。プリンシパル検証プロバイダの詳細については、「プリンシパル検証プロバイダ」を参照してください。
getIdentityAsserter
メソッドは、ID アサーション プロバイダの実行時クラス (IdentityAsserter
SSPI 実装) の参照を取得します。ほとんどの場合、このメソッドの戻り値は null
になります (例については コード リスト 3-1 を参照)。ID アサーション プロバイダの詳細については、「ID アサーション プロバイダ」を参照してください。
AuthenticationProvider
SSPI と上記メソッドの詳細については、「WebLogic Server 8.1 API リファレンス Javadoc」を参照してください。
JAAS javax.security.auth.spi.LoginModule
インタフェースを実装するには、以下のメソッドを実装する必要があります。
public void initialize (Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options)
initialize
メソッドは LoginModule を初期化します。引数として取るのは、結果として得られるプリンシパルを格納するサブジェクト、認証プロバイダが認証情報のコンテナにコールバックするのに使用する CallbackHandler
、任意の共有状態情報のマップ、およびコンフィグレーション オプション (すなわち、LoginModule に渡したい任意の付加的情報) のマップです。
CallbackHandler
は、可変個の引数を複合オブジェクトとしてメソッドに渡すことができるようにする高度に柔軟な JAAS 規格です。CallbackHandler
の詳細については、Java 2 Enterprise Edition, v1.4.1 API 仕様 Javadoc を参照して CallbackHandler インタフェースを調べてください。
login
メソッドは、ユーザを認証し、認証情報のコンテナにコールバックすることでそのユーザのプリンシパルを作成しようと試みます。複数の LoginModule が複数の認証プロバイダの一部としてコンフィグレーションされている場合、このメソッドは LoginModule ごとにそのコンフィグレーションの順序で呼び出されます。ログインが成功したかどうか (すなわち、プリンシパルが作成されたかどうか) についての情報は、LoginModule ごとに格納されます。
commit
メソッドは、login()
メソッドで作成されたプリンシパルをサブジェクトに追加しようと試みます。このメソッドも、複数の認証プロバイダの一部としてコンフィグレーションされている LoginModule ごとに呼び出され、順番に実行されます。コミットが成功したかどうかの情報は、LoginModule ごとに格納されます。
abort
メソッドは、コンフィグレーションされている認証プロバイダの一部としてコンフィグレーションされている LoginModule のコミットが失敗した (つまり、関連する REQUIRED
、REQUISITE
、SUFFICIENT
、および OPTIONAL
LoginModule が成功しなかった) 場合に LoginModule ごとに呼び出されます。abort
メソッドは、LoginModule のプリンシパルをサブジェクトから削除し、実行されたアクションを効果的にロールバックします。利用可能な制御フラグの設定の詳細については、Java 2 Enterprise Edition, v1.4.1 API 仕様 Javadoc の「LoginModule インタフェース」を参照してください。
logout
メソッドは、ユーザをシステムからログアウトさせようとします。また、サブジェクトのリセットも行うので、関連付けられているプリンシパルは格納されなくなります。
注意 : LoginModule.logout
メソッドは WebLogic 認証プロバイダまたはカスタム認証プロバイダに対して呼び出されることはありません。これは単に、いったんプリンシパルが作成されサブジェクト内に入れられると、WebLogic Security フレームワークはこのサブジェクトのライフサイクルを制御しなくなるからです。したがって、LoginContext
を作成してログインおよびサブジェクトの取得を行う、開発者によって記述されたユーザ コードもまた、LoginContext.logout
メソッドを呼び出す必要があります。ユーザ コードが、JAAS を直接使用する Java クライアントで実行される場合、このコードには LoginContext.logout
メソッドを呼び出すというオプションがあります。これにより、サブジェクトはクリアされます。ユーザ コードがサーブレットで実行される場合、サーブレットはサーブレット セッションからユーザをログアウトすることができます。これにより、サブジェクトはクリアされます。
JAAS LoginModule
インタフェースと上記メソッドの詳細については、『Java Authentication and Authorization Service (JAAS) 1.0 Developer's Guide』および Java 2 Enterprise Edition, v1.4.1 API 仕様 Javadoc の「LoginModule インタフェース」を参照してください。
記述した LoginModule からカスタム例外を送出することができます。そうすると、カスタム例外をアプリケーションおよび、実行された適切なアクションで捕捉できます。たとえば、LoginModule から PasswordChangeRequiredException
が送出されると、その例外をアプリケーションで捕捉して、パスワードの変更が可能なページへユーザを転送するのに使用できます。
LoginModule からカスタム例外を送出し、それをアプリケーション内で捕捉する場合には、以下のことを確認する必要があります。
コード リスト 3-1 は、サンプル認証プロバイダの 2 つの実行時クラスの 1 つである SampleAuthenticationProviderImpl.java
を示しています。実行時クラスには以下の実装が含まれています。
initialize
、getDescription
、および shutdown
という SecurityProvider
インタフェースから継承した 3 つのメソッド (「「Provider」SSPI の目的を理解する」を参照)getLoginModuleConfiguration
、getAssertionModuleConfiguration
、getPrincipalValidator
、および getIdentityAsserter
という AuthenticationProvider
SSPI の 4 つのメソッド (「AuthenticationProvider SSPI を実装する」を参照)注意 : コード リスト 3-1 の太字のコードは、クラス宣言とメソッド シグネチャを示しています。
コード リスト 3-1 SampleAuthenticationProviderImpl.java
package examples.security.providers.authentication;
import java.util.HashMap;
import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag;
import weblogic.management.security.ProviderMBean;
import weblogic.security.provider.PrincipalValidatorImpl;
import weblogic.security.spi.AuthenticationProvider;
import weblogic.security.spi.IdentityAsserter;
import weblogic.security.spi.PrincipalValidator;
import weblogic.security.spi.SecurityServices;
public final class SampleAuthenticationProviderImpl implements AuthenticationProvider
{
private String description;
private SampleAuthenticatorDatabase database;
private LoginModuleControlFlag controlFlag;
public void initialize(ProviderMBean mbean, SecurityServices services)
{
System.out.println("SampleAuthenticationProviderImpl.initialize");
SampleAuthenticatorMBean myMBean = (SampleAuthenticatorMBean)mbean;
description = myMBean.getDescription() + "\n" + myMBean.getVersion();
database = new SampleAuthenticatorDatabase(myMBean);
String flag = myMBean.getControlFlag();
if (flag.equalsIgnoreCase("REQUIRED")) {
controlFlag = LoginModuleControlFlag.REQUIRED;
} else if (flag.equalsIgnoreCase("OPTIONAL")) {
controlFlag = LoginModuleControlFlag.OPTIONAL;
} else if (flag.equalsIgnoreCase("REQUISITE")) {
controlFlag = LoginModuleControlFlag.REQUISITE;
} else if (flag.equalsIgnoreCase("SUFFICIENT")) {
controlFlag = LoginModuleControlFlag.SUFFICIENT;
} else {
throw new IllegalArgumentException("invalid flag value" + flag);
}
}
public String getDescription()
{
return description;
}
public void shutdown()
{
System.out.println("SampleAuthenticationProviderImpl.shutdown");
}
private AppConfigurationEntry getConfiguration(HashMap options)
{
options.put("database", database);
return new
AppConfigurationEntry(
"examples.security.providers.authentication.SampleLoginModuleImpl",
controlFlag,
options
);
}
public AppConfigurationEntry getLoginModuleConfiguration()
{
HashMap options = new HashMap();
return getConfiguration(options);
}
public AppConfigurationEntry getAssertionModuleConfiguration()
{
HashMap options = new HashMap();
options.put("IdentityAssertion","true");
return getConfiguration(options);
}
public PrincipalValidator getPrincipalValidator()
{
return new PrincipalValidatorImpl();
}
public IdentityAsserter getIdentityAsserter()
{
return null;
}
}
コード リスト 3-2 は、サンプル認証プロバイダの 2 つの実行時クラスの 1 つである SampleLoginModuleImpl.java
を示しています。この実行時クラスは、JAAS LoginModule インタフェースを実装し (「JAAS LoginModule インタフェースを実装する」を参照)、したがってその initialize
、login
、commit
、abort
、および logout
メソッドの実装が含まれています。
注意 : コード リスト 3-2 の太字のコードは、クラス宣言とメソッド シグネチャを示しています。
コード リスト 3-2 SampleLoginModuleImpl.java
package examples.security.providers.authentication;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Map;
import java.util.Vector;
import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginException;
import javax.security.auth.login.FailedLoginException;
import javax.security.auth.spi.LoginModule;
import weblogic.management.utils.NotFoundException;
import weblogic.security.spi.WLSGroup;
import weblogic.security.spi.WLSUser;
import weblogic.security.principal.WLSGroupImpl;
import weblogic.security.principal.WLSUserImpl;
final public class SampleLoginModuleImpl implements LoginModule
{
private Subject subject;
private CallbackHandler callbackHandler;
private SampleAuthenticatorDatabase database;
// ログインかどうかを判別するか、ID を断定する
private boolean isIdentityAssertion;
// 認証ステータス
private boolean loginSucceeded;
private boolean principalsInSubject;
private Vector principalsForSubject = new Vector();
public void initialize(Subject subject, CallbackHandler callbackHandler, Map
sharedState, Map options)
{
// コンストラクタの後でログインの前にのみ、一度だけ呼び出される
System.out.println("SampleLoginModuleImpl.initialize");
this.subject = subject;
this.callbackHandler = callbackHandler;
// ID アサーション オプションを確認する
isIdentityAssertion =
"true".equalsIgnoreCase((String)options.get("IdentityAssertion"));
database = (SampleAuthenticatorDatabase)options.get("database");
}
public boolean login() throws LoginException
{
// initialize の後にのみ、一度だけ呼び出される
System.out.println("SampleLoginModuleImpl.login");
// loginSucceeded は false
// principalsInSubject は false
// user は null
// group は null
Callback[] callbacks = getCallbacks();
String userName = getUserName(callbacks);
if (userName.length() > 0) {
if (!database.userExists(userName)) {
throwFailedLoginException("Authentication Failed: User " + userName
+ " doesn't exist.");
}
if (!isIdentityAssertion) {
String passwordWant = null;
try {
passwordWant = database.getUserPassword(userName);
} catch (NotFoundException shouldNotHappen) {}
String passwordHave = getPasswordHave(userName, callbacks);
if (passwordWant == null || !passwordWant.equals(passwordHave)) {
throwFailedLoginException(
"Authentication Failed: User " + userName + " bad password. " +
"Have " + passwordHave + ". Want " + passwordWant + "."
);
}
}
} else {
// 匿名ログイン? 許可する?
System.out.println("\tempty userName");
}
loginSucceeded = true;
principalsForSubject.add(new WLSUserImpl(userName));
addGroupsForSubject(userName);
return loginSucceeded;
}
public boolean commit() throws LoginException
{
// ログインの後にのみ、一度だけ呼び出される
// loginSucceeded は true または false
// principalsInSubject は false
// user は !loginSucceeded の場合は null、それ以外の場合は null または非 null
// group は user == null の場合は null、それ以外の場合は null または非 null
System.out.println("SampleLoginModule.commit");
if (loginSucceeded) {
subject.getPrincipals().addAll(principalsForSubject);
principalsInSubject = true;
return true;
} else {
return false;
}
}
public boolean abort() throws LoginException
{
// 認証プロセスを中止するために abort メソッドが呼び出される
// これはフェーズ 1 が失敗した場合の、認証のフェーズ 2 であり、
// LoginContext の全体的な認証が失敗した場合に呼び出される
// loginSucceeded は true または false
// user は !loginSucceeded の場合は null、それ以外の場合は null または非 null
// group は user == null の場合は null、それ以外の場合は null または非 null
// principalsInSubject は user が null の場合は false、それ以外の場合は true
// または false
System.out.println("SampleLoginModule.abort");
if (principalsInSubject) {
subject.getPrincipals().removeAll(principalsForSubject);
principalsInSubject = false;
}
return true;
}
public boolean logout() throws LoginException
{
// 呼び出し禁止
System.out.println("SampleLoginModule.logout");
return true;
}
private void throwLoginException(String msg) throws LoginException
{
System.out.println("Throwing LoginException(" + msg + ")");
throw new LoginException(msg);
}
private void throwFailedLoginException(String msg) throws FailedLoginException
{
System.out.println("Throwing FailedLoginException(" + msg + ")");
throw new FailedLoginException(msg);
}
private Callback[] getCallbacks() throws LoginException
{
if (callbackHandler == null) {
throwLoginException("No CallbackHandler Specified");
}
if (database == null) {
throwLoginException("database not specified");
}
Callback[] callbacks;
if (isIdentityAssertion) {
callbacks = new Callback[1];
} else {
callbacks = new Callback[2];
callbacks[1] = new PasswordCallback("password: ",false);
}
callbacks[0] = new NameCallback("username: ");
try {
callbackHandler.handle(callbacks);
} catch (IOException e) {
throw new LoginException(e.toString());
} catch (UnsupportedCallbackException e) {
throwLoginException(e.toString() + " " + e.getCallback().toString());
}
return callbacks;
}
private String getUserName(Callback[] callbacks) throws LoginException
{
String userName = ((NameCallback)callbacks[0]).getName();
if (userName == null) {
throwLoginException("Username not supplied.");
}
System.out.println("\tuserName\t= " + userName);
return userName;
}
private void addGroupsForSubject(String userName)
{
for (Enumeration e = database.getUserGroups(userName);
e.hasMoreElements();) {
String groupName = (String)e.nextElement();
System.out.println("\tgroupName\t= " + groupName);
principalsForSubject.add(new WLSGroupImpl(groupName));
}
}
private String getPasswordHave(String userName, Callback[] callbacks) throws
LoginException
{
PasswordCallback passwordCallback = (PasswordCallback)callbacks[1];
char[] password = passwordCallback.getPassword();
passwordCallback.clearPassword();
if (password == null || password.length < 1) {
throwLoginException("Authentication Failed: User " + userName + ".
Password not supplied");
}
String passwd = new String(password);
System.out.println("\tpasswordHave\t= " + passwd);
return passwd;
}
}
カスタム セキュリティ プロバイダの MBean タイプを生成する前に、以下の作業が必要です。
この情報を理解し、設計に関する判断を下したら、次の手順でカスタム認証プロバイダの MBean タイプを作成します。
注意 : これらの手順の実行方法は、いくつかのサンプル セキュリティ プロバイダ (dev2dev Web サイトの「Code Samples: WebLogic Server」で入手可能) に示されています。
この節で説明する手順はすべて、Windows 環境での作業を想定しています。
MBean 定義ファイル (MDF) を作成するには、次の手順に従います。
注意 : MDF 要素の構文についての完全なリファレンスは、「MBean 定義ファイル (MDF) 要素の構文」に収められています。
MDF を作成したら、WebLogic MBeanMaker を使用してそれを実行できます。WebLogic MBeanMaker は現在のところコマンドライン ユーティリティで、入力として MDF を受け取り、MBean インタフェース、MBean 実装、関連する MBean 情報ファイルなどの中間 Java ファイルをいくつか出力します。これらの中間ファイルが合わさって、カスタム セキュリティ プロバイダの MBean タイプになります。
MBean タイプの作成手順は、カスタム認証プロバイダの設計に応じて異なります。必要な設計に合わせて適切な手順を実行してください。
カスタム認証プロバイダの MDF に任意 SSPI MBean もカスタム操作も実装しない場合、次の手順に従います。
WL_HOME\server\lib\weblogic.jar
WL_HOME\server\lib\mbeantypes\wlManagement.jar
JAVA_HOME\..\lib\tools.jar
-Dfiles
フラグで指定する filedirjava -DMDF=
xmlfile -Dfiles=
filesdir -DcreateStubs=true weblogic.management.commo.WebLogicMBeanMaker
ここで、-DMDF
フラグは WebLogic MBeanMaker が MDF をコードに変換すべきであることを示し、xmlFile は MDF (XML MBean の記述ファイル)、filesdir は WebLogic MBeanMaker で作成された MBean タイプの中間ファイルが格納される場所を示します。
-DMDF
フラグで指定された xml ファイルと同じディレクトリに WL_HOME\server\lib\commo.dtd
をコピーします。
xmlfile が入力されるたびに、新しい出力ファイル群が生成されます。
-DcreateStubs=true
フラグを使用するたびに、既存の MBean 実装ファイルがすべて上書きされます。
注意 : WebLogic MBeanMaker では MDF を一度に 1 つ処理します。したがって、MDF (つまり認証プロバイダ) が複数ある場合には、このプロセスを繰り返す必要があります。
カスタム認証プロバイダの MDF に任意 SSPI MBean またはカスタム操作を実装する場合、以下の質問に答えながら手順を進めてください。
WL_HOME\server\lib\weblogic.jar
WL_HOME\server\lib\mbeantypes\wlManagement.jar
JAVA_HOME\..\lib\tools.jar
-Dfiles
フラグで指定する filedirjava -DMDF=
xmlfile -Dfiles=
filesdir -DcreateStubs=true weblogic.management.commo.WebLogicMBeanMaker
ここで、-DMDF
フラグは WebLogic MBeanMaker が MDF をコードに変換すべきであることを示し、xmlFile は MDF (XML MBean の記述ファイル)、filesdir は WebLogic MBeanMaker で作成された MBean タイプの中間ファイルが格納される場所を示します。
-DMDF
フラグで指定された xml ファイルと同じディレクトリに WL_HOME\server\lib\commo.dtd
をコピーします。
xmlfile が入力されるたびに、新しい出力ファイル群が生成されます。
-DcreateStubs=true
フラグを使用するたびに、既存の MBean 実装ファイルがすべて上書きされます。
注意 : WebLogic MBeanMaker では MDF を一度に 1 つ処理します。したがって、MDF (つまり認証プロバイダ) が複数ある場合には、このプロセスを繰り返す必要があります。
WebLogic MBeanMaker によって生成される MBean 実装ファイルには、MBeanNameImpl.java
という名前が付けられます。 たとえば、SampleAuthenticator
という名前の MDF の場合、編集される MBean 実装ファイルは SampleAuthenticatorImpl.java
という名前になります。
WL_HOME\server\lib\weblogic.jar
WL_HOME\server\lib\mbeantypes\wlManagement.jar
JAVA_HOME\..\lib\tools.jar
-Dfiles
フラグで指定する filedirjava -DMDF=
xmlfile -Dfiles=
filesdir -DcreateStubs=true weblogic.management.commo.WebLogicMBeanMaker
ここで、-DMDF
フラグは WebLogic MBeanMaker が MDF をコードに変換すべきであることを示し、xmlFile は MDF (XML MBean の記述ファイル)、filesdir は WebLogic MBeanMaker で作成された MBean タイプの中間ファイルが格納される場所を示します。
-DMDF
フラグで指定された xml ファイルと同じディレクトリに WL_HOME\server\lib\commo.dtd
をコピーします。
xmlfile が入力されるたびに、新しい出力ファイル群が生成されます。
-DcreateStubs=true
フラグを使用するたびに、既存の MBean 実装ファイルがすべて上書きされます。
注意 : WebLogic MBeanMaker では MDF を一度に 1 つ処理します。したがって、MDF (つまり認証プロバイダ) が複数ある場合には、このプロセスを繰り返す必要があります。
WebLogic MBeanMaker によって生成される MBean 実装ファイルには、<MBeanName>Impl.java
という名前が付けられます。たとえば、SampleAuthenticator
という名前の MDF の場合、編集される MBean 実装ファイルは SampleAuthenticatorImpl.java
という名前になります。
これには、メソッドの実装を既存の MBean 実装ファイルから新しく生成された MBean 実装ファイルにコピー (または、新しく生成された MBean 実装ファイルから既存の MBean 実装ファイルに新しいメソッドを追加) し、いずれの MBean 実装ファイルにも入っているメソッドのメソッド シグネチャへの変更が使用する MBean 実装ファイルに反映されていることを確認するといった作業が必要です。
MBean インタフェース ファイルは、実行時クラスまたは MBean 実装ファイルがコンフィグレーション データを取得するために使用する MBean とのクライアント サイド API です。「「Provider」SSPI の目的を理解する」で説明されているように、これは initialize メソッドで使用するのが一般的です。
WebLogic MBeanMaker では、作成済みの MDF から MBean タイプを生成するので、生成される MBean インタフェース ファイルの名前は、その MDF 名の後に「MBean」というテキストが付いたものになります。たとえば、WebLogic MBeanMaker を使用して SampleAuthenticator
MDF を実行すると、MBean インタフェース ファイルの名前が SampleAuthenticatorMBean.java
になります。
WebLogic MBeanMaker を使用して MDF を実行して中間ファイルを生成し、MBean 実装ファイルを編集して適切なメソッドの実装を提供したら、カスタム認証プロバイダの MBean ファイルと実行時クラスを MBean JAR ファイル (MJF) にパッケージ化する必要があります。このプロセスも、WebLogic MBeanMaker によって自動化されます。
カスタム認証プロバイダの MJF を作成するには、次の手順を行います。
WL_HOME\server\lib\weblogic.jar
WL_HOME\server\lib\mbeantypes\wlManagement.jar
JAVA_HOME\..\lib\tools.jar
-Dfiles
フラグで指定する filedirjava -DMJF=
jarfile -Dfiles=
filesdir weblogic.management.commo.WebLogicMBeanMaker
ここで、-DMJF
フラグは WebLogic MBeanMaker が新しい MBean タイプを含む JAR ファイルを構築すべきであることを示し、jarfile は MJF の名前、filesdir は WebLogic MBeanMaker で MJF に JAR 化する対象ファイルが存在する場所を示します。
この時点でコンパイルが行われるので、エラーが発生するおそれがあります。jarfile が指定されていて、エラーが発生しなかった場合には、指定された名前の MJF が作成されます。
注意 : 既存の MJF を更新する場合は、MJF をいったん削除してから再生成します。WebLogic MBeanMaker にも -DIncludeSource
オプションがあり、それを指定すると、生成される MJF にソース ファイルを含めるかどうかを制御することができます。ソース ファイルには、生成されたソースと MDF そのものがあります。デフォルトは false
です。このオプションは、-DMJF
を使用しない場合には無視されます。
生成された MJF は、自らの WebLogic Server 環境にインストールすることも、顧客に配布してそれぞれの WebLogic Server 環境にインストールしてもらうこともできます。
MBean タイプを WebLogic Server 環境にインストールするには、MJF を WebLogic Server の WL_HOME\server\lib\mbeantypes
ディレクトリにコピーします。ここで、WL_HOME は WebLogic Server の最上位のインストール ディレクトリです。これで、カスタム認証プロバイダが「デプロイ」されます。つまり、カスタム認証プロバイダが WebLogic Server Administration Console から管理できるようになります。
注意 : MBean タイプをインストールするデフォルトのディレクトリは、WL_HOME\server\lib\mbeantypes
です。ただし、サーバを起動するときに -Dweblogic.alternateTypesDirectory=
<dir> コマンドライン フラグを使用すれば、WebLogic Server が追加ディレクトリで MBean タイプを検索します。<dir> は、ディレクトリ名のカンマ区切りのリストです。このフラグを使用する場合、WebLogic Server は常に最初に WL_HOME\server\lib\mbeantypes
から MBean タイプをロードします。たとえば、-Dweblogic.alternateTypesDirectory = dirX,dirY
の場合、WebLogic Server は最初に WL_HOME\server\lib\mbeantypes
から MBean タイプをロードしてから、dirX
および dirY
にある有効なアーカイブをロードします。WebLogic Server に追加ディレクトリで MBean タイプを検索するように指示する際に Java セキュリティ マネージャを使用している場合には、weblogic.policy
ファイルを更新して、MBean タイプ (その結果としてカスタム セキュリティ プロバイダ) に対する適切なパーミッションを付与することも必要になります。詳細については、『WebLogic Security プログラマーズ ガイド』の「Java セキュリティ マネージャを使用しての WebLogic リソースの保護」を参照してください。
カスタム認証プロバイダをコンフィグレーションすることによって (「Administration Console を使用してカスタム認証プロバイダをコンフィグレーションする」を参照)、MBean タイプのインスタンスを作成して、GUI、他の Java コード、または API からそれらの MBean インスタンスを使用することができます。たとえば、WebLogic Server Administration Console を使用して、属性を取得/設定したり操作を呼び出したりすることもできますし、他の Java オブジェクトを開発して、そのオブジェクトで MBean をインスタンス化し、それらの MBean から提供される情報に自動的に応答させることもできます。なお、これらの MBean インスタンスをバックアップしておくことをお勧めします。詳細については、『WebLogic Server のコンフィグレーションと管理』の「障害が発生したサーバの回復」の「セキュリティ データのバックアップ」を参照してください。
カスタム認証プロバイダをコンフィグレーションするということは、そのカスタム認証プロバイダをセキュリティ レルムに追加するということです。追加されたカスタム認証プロバイダには、認証サービスを必要とするアプリケーションからアクセスできます。
カスタム セキュリティ プロバイダのコンフィグレーションは管理タスクですが、カスタム セキュリティ プロバイダの開発者が行うこともできます。この節では、カスタム認証プロバイダのコンフィグレーション担当者にとって重要な情報を提供します。
注意 : WebLogic Server Administration Console を使用してカスタム認証プロバイダをコンフィグレーションする手順は、『WebLogic Security の管理』の「カスタム セキュリティ プロバイダのコンフィグレーション」で説明されています。
カスタム認証プロバイダを使用する一環として、ユーザ ロックアウトをコンフィグレーションおよび管理する方法を検討する必要があります。以下の 2 つの選択肢があります。
WebLogic Security フレームワークは、WebLogic Security フレームワークと直接連携してユーザ ロックアウトを管理するレルムワイドのユーザ ロックアウト マネージャを提供します。
注意 : レルムワイドのユーザ ロックアウト マネージャと WebLogic Server 6.1 PasswordPolicyMBean
(レルム アダプタ レベル) の両方をアクティブにすることができます。詳細については、WebLogic Server 6.1API リファレンス Javadoc の PasswordPolicyMBean インタフェースを参照してください。
レルムワイドのユーザ ロックアウト マネージャを使用する場合、それをカスタム認証プロバイダと連携させるためには、WebLogic Server Administration Console を使用して以下のことを行います。
注意 : ユーザ ロックアウト マネージャへの変更は、サーバを再起動するまで有効になりません。Administration Console を使用して上記のタスクを実行する手順は、『WebLogic Security の管理』の「ユーザ アカウントの保護」で説明されています。
カスタム認証プロバイダの一部として独自のユーザ ロックアウト マネージャを実装する場合は、次の手順を行う必要があります。
「LoginModule とさまざまな要素から成る認証」で説明されているように、認証プロセスの結果は、複数の認証プロバイダ (その結果として LoginModule) のコンフィグレーション順序によって影響を受けます。
認証プロバイダは任意の順序でコンフィグレーションできます。ただし、コンフィグレーション済みの認証プロバイダの順序を変更する必要がある場合は、『WebLogic Security の管理』の「認証プロバイダの順序の変更」で説明されている手順に従ってください。