3 認証プロバイダ
認証は、呼出し側が、特定のユーザーまたはシステムのかわりに動作していることを証明する際に使用するメカニズムです。認証は、ユーザー名とパスワードの組合せなどの資格証明を使用して「あなたは誰」という問いに答えます。
WebLogic Serverでは、ユーザーまたはシステム・プロセスのIDを証明するために認証プロバイダを使用します。認証プロバイダでは、ID情報を記憶したり、トランスポートしたり、その情報が必要な場合にサブジェクトを通じてシステムの様々なコンポーネントで利用できるようにしたりします。認証プロセスでは、プリンシパル検証プロバイダが、サブジェクト内に格納されるプリンシパル(ユーザーおよびグループ)のセキュリティを強化するため、それらのプリンシパルに署名して信頼性を検証します。(「プリンシパル検証プロバイダ」を参照してください。)
この章の内容は次のとおりです。
-
ノート:
IDアサーション・プロバイダは、ユーザーまたはシステム・プロセスがトークンを使用してそれぞれのIDをアサートする特殊な形態の認証プロバイダです。「IDアサーション・プロバイダ」を参照してください。
認証の概念
カスタム認証プロバイダの開発の詳細に立ち入る前に、次の概念を理解しておくことが大切です。
ユーザー/グループ、プリンシパル、サブジェクト
ユーザーは、人を表すという点でオペレーティング・システムのユーザーに似ています。一方、グループはユーザーのカテゴリで、肩書きなどの共通の特徴で分類されたものです。ユーザーをグループに分類すると、多数のユーザーに対するアクセス許可を管理しやすくなります。『Oracle WebLogic Serverロールおよびポリシーによるリソースの保護』のユーザー、グループおよびセキュリティ・ロールに関する項を参照してください。
WebLogic Serverのようなアプリケーション・サーバーでは、ユーザーもグループもプリンシパルとして使用することができます。プリンシパルは、認証の結果としてユーザーまたはグループに割り当てられるIDです。JAAS (Java Authentication and Authorization Service)では、プリンシパルなどの認証情報のコンテナとしてサブジェクトを使用することになっています。同じサブジェクト内に格納されている各プリンシパルは、ある人の財布に入っている複数のカードのように、同じユーザーのIDを異なる観点で表しています。たとえば、持ち主の身元を銀行に示すためにATMカードがあり、所属する組織に示すためにメンバーシップ・カードがある、という具合です。「Java Authentication and Authorization Service (JAAS)」を参照してください。
ノート:
サブジェクトは、WebLogic Server 6.xのユーザーに取ってかわるものです。
図3-1に、ユーザー、グループ、プリンシパル、およびサブジェクト間の関係を示します。
認証に成功すると、その一環として、プリンシパルは署名され、その後の使用に備えてサブジェクトに格納されます。プリンシパルに署名するのはプリンシパル検証プロバイダであり、実際にプリンシパルをサブジェクトに格納するのは認証プロバイダのLoginModuleです。後で、サブジェクト内に格納されたプリンシパルに呼出し側がアクセスしようとすると、そのプリンシパルが署名されてから変更されていないことをプリンシパル検証プロバイダが確認したうえで、そのプリンシパルが呼出し側に返されます(それ以外のセキュリティ条件がすべて満たされている場合)。
ノート:
「プリンシパル検証プロバイダ」および「LoginModules」をそれぞれ参照してください。
WebLogic Serverユーザーまたはグループを表すプリンシパルは、weblogic.security.spi
パッケージのWLSUser
インタフェースとWLSGroup
インタフェースを実装する必要があります。
初期ユーザーおよびグループの指定
認証プロバイダでは、実行中のWebLogic Serverで認証を実行する前にユーザーとグループのリストが必要です。一部の認証プロバイダを使用すると、管理者は外部データベースを構成し(LDAPサーバーまたはDBMSにユーザーおよびグループを追加するなど)、そのデータベースを使用するようにプロバイダを構成できます。管理者が外部データベースのツールを使用してユーザーとグループをあらかじめ指定するので、これらのプロバイダはどのようにユーザーとグループが指定されたかを認識する必要はありません。
ただし、一部の認証プロバイダは、独自のユーザーとグループのリストを作成および管理します。ManageableSampleAuthenticatorプロバイダがそれに当てはまります。この場合、認証プロバイダは、ユーザーとグループの初期セットがどのように指定されたかを認識する必要があります。これを行う方法としては、プロバイダの初期化メソッドで、ユーザーとグループがまだないことを通知し、ユーザーとグループの初期セットを指定してリストを作成するというものがあります。
一部のプロバイダは、セキュリティ・レルムごとにユーザーとグループの個別のリストを持っているので、新しいレルムで最初にリストを使用する場合に、ユーザーとグループの初期セットを作成する必要があります。たとえば、ManageableSampleAuthenticatorプロバイダは、ユーザーとグループの個別のプロパティ・ファイルをレルムごとに作成します。この認証プロバイダのinitialize
メソッドは、レルム名を取得して、そのレルムのプロパティ・ファイルが存在するかどうかを判別し、存在しない場合は、ユーザーとグループの初期セットでプロパティ・ファイルを作成します。
LoginModule
LoginModuleは、認証プロバイダの必須コンポーネントであり、境界認証用に別個のLoginModuleを開発する必要がある場合はIDアサーション・プロバイダのコンポーネントにもなります。
LoginModuleは、認証処理における「馬車馬」のような存在です。すべてのLoginModuleは、セキュリティ・レルム内のユーザーの認証と、サブジェクト内への必要なプリンシパル(ユーザー/グループ)の格納を担当します。境界認証に使用されないLoginModuleも、提示された証明情報(たとえば、ユーザーのパスワード)が正しいかどうかを確認します。
ノート:
「IDアサーション・プロバイダ」を参照してください。
セキュリティ・レルムに複数の認証プロバイダが構成されている場合、各認証プロバイダのLoginModuleは同じサブジェクトにプリンシパルを格納します。したがって、ある認証プロバイダのLoginModuleによって、WebLogic Serverユーザー(WLSUser
インタフェースの実装)を表すプリンシパル「Joe」がサブジェクトに追加された場合、セキュリティ・レルム内の他のすべての認証プロバイダは、「Joe」に出会ったときに同じ人物を参照する必要があります。つまり、他の認証プロバイダのLoginModuleは、同じ人物を参照するために、WebLogic Serverユーザーを表す別のプリンシパル(「Joseph」など)をサブジェクトに追加しようとしてはなりません。ただし、別の認証プロバイダのLoginModuleは、WLSUser
以外のタイプのプリンシパルを「Joseph」という名前で追加することができます。
LoginModuleインタフェース
LoginModuleは、ユーザー名とパスワードの組合せ、スマート・カード、バイオメトリック装置などの様々な認証メカニズムを扱うように作成することができます。LoginModuleを開発するには、JAAS (Java Authentication and Authorization Service)に基づき、認証情報のコンテナとしてサブジェクトを使用するjavax.security.auth.spi.LoginModule
インタフェースを実装します。LoginModule
インタフェースを使用すると、単一アプリケーション用に様々な種類の認証技術をプラグインすることができ、WebLogicセキュリティ・フレームワークはマルチパート認証用に複数のLoginModule
実装をサポートするように設計されています。さらに、LoginModuleインスタンス間に依存関係を設けたり、それらのインスタンス間で資格証明を共有することもできます。ただし、LoginModuleと認証プロバイダの関係は1対1です。つまり、網膜スキャン認証を扱うLoginModuleと、スマート・カードのようなハードウェア・デバイスとのインタフェースを取るLoginModuleを用意するには、それぞれにLoginModule
インタフェースの実装が含まれる2つの認証プロバイダを開発して構成する必要があります。「JAAS LoginModuleインタフェースの実装」を参照してください。
ノート:
LoginModuleは、独自に開発するのではなくサード・パーティのセキュリティ・ベンダーから入手することもできます。
LoginModuleとマルチパート認証
複数の認証プロバイダ(その結果として複数のLoginModule)を構成する方法は、認証プロセスの全体的な結果に影響します。これは、様々な要素からなる認証で特に重要です。まず、LoginModuleは認証プロバイダのコンポーネントであるため、認証プロバイダが構成された順序で呼び出されます。通常、認証プロバイダの構成には、WebLogic Server管理コンソールを使用します。(「認証プロバイダの順序の指定」を参照してください。)次に、各LoginModuleの制御フラグがどのように設定されるかによって、認証プロセスでのエラーの処理方法が決まります。図3-2は、それぞれ別の認証プロバイダに属する3つのLoginModuleが関わるサンプル・フローを示すとともに、様々な認証結果でサブジェクトに何が起こるのかを示しています。
カスタム認証プロバイダ#1の制御フラグがREQUIREDに設定されていた場合、ユーザー認証ステップで認証が失敗すると、認証プロセス全体が失敗します。また、ユーザーがWebLogic認証プロバイダ(またはカスタム認証プロバイダ#2)によって認証されなかった場合、認証プロセス全体が失敗します。認証プロセスがこのように失敗した場合、3つのLoginModuleすべてがロールバックされ、サブジェクトにプリンシパルが格納されません。
ノート:
Java Authentication and Authorization Service (JAAS) LoginModule開発者ガイド(http://docs.oracle.com/javase/8/docs/technotes/guides/security/jaas/JAASLMDevGuide.html
)およびLoginModuleインタフェースに関する項(http://docs.oracle.com/javase/8/docs/api/javax/security/auth/spi/LoginModule.html
)をそれぞれ参照してください。
JAAS (Java Authentication and Authorization Service)
クライアントが認証の必要なアプリケーション、アプレット、Enterprise JavaBean (EJB)、あるいはサーブレットのいずれであろうと、WebLogic Serverでは、JAAS (Java Authentication and Authorization Service)クラスを用いて、信頼性とセキュリティを確保しつつクライアントに対する認証を行います。JAASは、プラガブルな認証モジュール(PAM)フレームワークのJavaバージョンを実装します。このフレームワークにより、アプリケーションは基底の認証技術から独立することができます。このため、PAMフレームワークを利用することで、アプリケーションに修正を加えることなく新しいまたは更新された認証技術を使用することができます。
WebLogic Serverは、リモートのファット・クライアントの認証および内部の認証でJAASを使用します。したがって、JAASに直に関与する必要があるのは、カスタム認証プロバイダの開発者とリモート・ファット・クライアント・アプリケーションの開発者だけです。シン・クライアントのユーザーまたはコンテナ内のファット・クライアント・アプリケーション(サーブレットからEnterprise JavaBeanを呼び出すものなど)の開発者は、JAASを直接使用したり、その知識を身につけたりする必要はありません。
JAASがWebLogicセキュリティ・フレームワークとどう連携するか
通常、JAASクラスとWebLogicセキュリティ・フレームワークを用いた認証は、以下のように実行されます:
-
クライアント側アプリケーションがユーザーまたはシステム・プロセスから認証情報を取得します。このときのメカニズムは、クライアントのタイプごとに異なります。
-
クライアント側アプリケーションでは、認証情報が格納された
CallbackHandler
を任意に作成できます。-
クライアント側アプリケーションは、
LoginContext
クラスを使用してCallbackHandler
をローカル(クライアント側) LoginModuleに渡します。ローカルLoginModuleは、WebLogic Serverの一部として提供されているUsernamePasswordLoginModule
の場合があります。 -
ローカルLoginModuleは、認証情報が格納された
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インタフェースに関する項(http://docs.oracle.com/javase/8/docs/api/javax/security/auth/callback/CallbackHandler.html
)を参照してください。LoginContextクラスに関する項(
http://docs.oracle.com/javase/8/docs/api/javax/security/auth/login/LoginContext.html
)を参照してください。UsernamePasswordLoginModuleクラスについては、Oracle WebLogic Server Java APIリファレンスを参照してください。
クライアント側LoginModuleを使用しない場合、ユーザー名とパスワードを他の方法で(たとえば初期JNDIルックアップの一部として)指定できます。
-
-
WebLogic Serverコンテナは、WebLogicセキュリティ・フレームワークに働きかけを行います。認証情報が格納されたクライアント側
CallbackHandler
が存在する場合、それがWebLogicセキュリティ・フレームワークに渡されます。 -
WebLogicセキュリティ・フレームワークにより、渡された認証情報を使用して、構成されている認証プロバイダごとに
CallbackHandler
が作成されます。これらはWebLogicセキュリティ・フレームワークによってサーバー側に作成された内部的なCallbackHandler
であり、クライアントのCallbackHandler
とは関係ありません。 -
WebLogicセキュリティ・フレームワークは、認証プロバイダに関連付けられたLoginModule (認証情報を処理するために指定された特定のLoginModule)を呼び出します。
ノート:
「LoginModules」を参照してください。
LoginModuleが認証情報を利用してクライアントを認証しようとします。
-
認証に成功すると、以下の処理が行われます。
-
プリンシパル検証プロバイダによってプリンシパル(ユーザーおよびグループ)が署名され、プログラムによるサーバー呼出し間での信頼性が確保されます。「プリンシパル検証プロバイダ」を参照してください。
-
LoginModuleは署名済みのプリンシパルをサブジェクトに関連付けます。そのオブジェクトが認証対象のユーザーまたはシステム・プロセスを表します。「ユーザー/グループ、プリンシパル、サブジェクト」を参照してください。
ノート:
サーバー側ですべてが行われる認証の場合、プロセスはステップ3から始まり、WebLogic Serverコンテナはステップ4の前に
weblogic.security.services.authentication.login
メソッドを呼び出します。
-
例:スタンドアロンのT3アプリケーション
スタンドアロンのT3アプリケーションの場合にJAASクラスがWebLogicセキュリティ・フレームワークとどう連携するかを図3-3に示し、続いてそれについて説明します。
この例で、JAASクラスとWebLogicセキュリティ・フレームワークを用いた認証は以下のように実行されます。
-
T3アプリケーションがユーザーまたはシステム・プロセスから認証情報(ユーザー名、パスワード、およびURL)を取得します。
-
T3アプリケーションは、認証情報が格納された
CallbackHandler
を作成します。-
T3アプリケーションは、
LoginContext
クラスを使用してCallbackHandler
をUsernamePasswordLoginModule
に渡します。ノート:
weblogic.security.auth.login.UsernamePasswordLoginModule
は標準のJAASjavax.security.auth.spi.LoginModule
インタフェースを実装し、クライアント側のAPIを使用してWebLogic Serverインスタンスに対するWebLogicクライアントの認証を行います。これは、T3クライアントとIIOPクライアントの両方で使用できます。このLoginModuleの呼出し側では、CallbackHandlerを実装してユーザー名(NameCallback)、パスワード(PasswordCallback)、およびURL (URLCallback)を渡す必要があります。 -
UsernamePasswordLoginModule
は、認証情報(ユーザー名、パスワード、およびURL)が格納されたCallbackHandler
をWebLogic Server RMIコンテナに渡します。
-
-
WebLogic Server RMIコンテナは、WebLogicセキュリティ・フレームワークに働きかけを行います。認証情報が格納されたクライアント側
CallbackHandler
がWebLogicセキュリティ・フレームワークに渡されます。 -
WebLogicセキュリティ・フレームワークにより、渡されたユーザー名、パスワード、およびURLの格納された
CallbackHandler
が、構成されている認証プロバイダごとに作成されます。これらはWebLogicセキュリティ・フレームワークによってサーバー側に作成された内部的なCallbackHandler
であり、クライアントのCallbackHandler
とは関係ありません。 -
WebLogicセキュリティ・フレームワークは、認証プロバイダに関連付けられたLoginModule (認証情報を処理するために指定された特定のLoginModule)を呼び出します。
LoginModuleが認証情報を利用してクライアントを認証しようとします。
-
認証に成功すると、以下の処理が行われます。
-
プリンシパル検証プロバイダによってプリンシパル(ユーザーおよびグループ)が署名され、プログラムによるサーバー呼出し間での信頼性が確保されます。
-
LoginModuleは署名済みのプリンシパルをサブジェクトに関連付けます。そのオブジェクトが認証対象のユーザーまたはシステムを表します。
-
WebLogicセキュリティ・フレームワークは認証ステータスをT3クライアント・アプリケーションに返し、T3クライアント・アプリケーションは認証済みのサブジェクトをWebLogicセキュリティ・フレームワークから受け取ります。
-
認証プロセス
図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認証プロバイダを使用すると、ユーザーとグループ・メンバーシップを編集、表示、および管理できます。
WebLogic Serverでは、デフォルトのセキュリティ・レルムにおいて、次の認証プロバイダをWebLogic認証プロバイダのかわりとして使用したり、WebLogic認証プロバイダと併用したりすることもできます。
-
Open LDAP、iPlanet、Microsoft Active Directory、およびNovell NDSといった外部LDAPストアにアクセスする一連のLDAP認証プロバイダ
-
認証を行うために、データベースに格納されているユーザー、パスワード、グループ、およびグループ・メンバーシップ情報にアクセスする一連のデータベース管理システム(DBMS)プロバイダ
-
認証を行うためにWindows NTユーザーおよびグループを使用するWindows NT認証プロバイダ
-
LDAP X509 IDアサーション・プロバイダ
デフォルトでは、これらの認証プロバイダは使用可能ですが、WebLogicデフォルト・セキュリティ・レルムには構成されていません。
追加の認証タスクを実行する場合は、カスタム認証プロバイダを開発する必要があります。
ノート:
初期状態ではサポートされていない種類のトークン(新規、カスタム、サード・パーティのトークンなど)を使用して境界認証を行うには、カスタムIDアサーション・プロバイダを開発する必要があります。「IDアサーション・プロバイダ」を参照してください。
カスタム認証プロバイダの開発方法
WebLogic認証プロバイダが開発者のニーズを満たさない場合、次のステップでカスタム認証プロバイダを開発することができます。
-
「WebLogic MBeanMakerを使用してMBeanタイプを生成する」で説明されているステップを実行してカスタム認証プロバイダのMBeanタイプを生成
適切なSSPIによるランタイム・クラスの作成
ランタイム・クラスを作成する前に、以下の作業が必要です。
この情報を理解し、設計に関する判断を下したら、次のステップでカスタム認証プロバイダのランタイム・クラスを作成します。
カスタム認証プロバイダのランタイム・クラスの作成例については、「例:サンプル認証プロバイダのランタイム・クラスの作成」を参照してください。
AuthenticationProviderV2 SSPIの実装
ノート:
AuthenticationProvider
SSPIは、このリリースのWebLogic Serverでは非推奨になっています。AuthenticationProviderV2
SSPIをかわりに使用してください。
AuthenticationProviderV2
SSPIを実装するには、Provider SSPIの目的についてで説明されているメソッドおよび次のメソッドの実装を提供する必要があります。
-
getLoginModuleConfiguration
public AppConfigurationEntry getLoginModuleConfiguration()
getLoginModuleConfiguration
メソッドは、認証プロバイダの関連付けられたLoginModuleに関する情報を取得します。その情報は、AppConfigurationEntry
として返されます。AppConfigurationEntry
は、LoginModuleのクラス名、認証プロバイダの関連するMBeanを通じて渡されたLoginModuleの制御フラグ、および他の構成情報をLoginModuleに渡すことを可能にするLoginModuleの構成オプション・マップの格納されたJAAS (Java Authentication and Authorization Service)クラスです。(
javax.security.auth.login
パッケージ内にある)AppConfigurationEntry
クラスとLoginModulesの制御フラグ・オプションの詳細は、AppConfigurationEntryクラスに関する項(http://docs.oracle.com/javase/8/docs/api/javax/security/auth/login/AppConfigurationEntry.html
)およびConfigurationクラスに関する項(http://docs.oracle.com/javase/8/docs/api/javax/security/auth/login/Configuration.html
)を参照してください。「LoginModules」および「MBeanタイプが必要な理由について」を参照してください。 -
getAssertionModuleConfiguration
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を変更する、またはフラグを -1に設定してキャッシュを無効にする)ことができます。トークンが有効なだけでなく、ユーザーも引続き有効であること(ユーザーが削除されていないことなど)を確認するのは、IDアサーション・プロバイダの役割です。
EJBの<run-as-principal>要素とカスタム認証プロバイダを併用する場合には、
getAssertionModuleConfiguration()
メソッドを使用します。このメソッドが実行するIDアサーションによって、<run-as-principal>要素に指定されているプリンシパルが検証されます。 -
getPrincipalValidator
public PrincipalValidator getPrincipalValidator()
getPrincipalValidator
メソッドは、プリンシパル検証プロバイダのランタイム・クラス(PrincipalValidator
SSPI実装)の参照を取得します。WebLogicプリンシパル検証プロバイダは、ほとんどの場合に使用できます。WebLogicプリンシパル検証プロバイダの返し方の例については、例3-1を参照してください。「プリンシパル検証プロバイダ」を参照してください。 -
getIdentityAsserter
public IdentityAsserterV2 getIdentityAsserter()
AuthenticationProviderV2の
getIdentityAsserter
メソッドは、新しいIDアサーション・プロバイダのランタイム・クラス(IdentityAsserterV2
SSPI実装)の参照を取得します。ほとんどの場合、このメソッドの戻り値は
null
になります(例については例3-1を参照)。「IDアサーション・プロバイダ」を参照してください。
前述のAuthenticationProviderV2
SSPIおよびメソッドの詳細は、Oracle WebLogic Server Java APIリファレンスを参照してください。
JAAS LoginModuleインタフェースの実装
JAAS javax.security.auth.spi.LoginModule
インタフェースを実装するには、以下のメソッドを実装する必要があります。
-
initialize
public void initialize (Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options)
initialize
メソッドはLoginModuleを初期化します。引数として取るのは、結果として得られるプリンシパルを格納するサブジェクト、認証プロバイダが認証情報のコンテナにコールバックするのに使用するCallbackHandler
、任意の共有状態情報のマップ、および構成オプション(すなわち、LoginModuleに渡す任意の付加的情報)のマップです。CallbackHandler
は、可変個の引数を複合オブジェクトとしてメソッドに渡すことができるようにする高度に柔軟なJAAS規格です。Java SE 8.0 API仕様のCallbackHandler
インタフェースに関する項(http://docs.oracle.com/javase/8/docs/api/javax/security/auth/callback/CallbackHandler.html
)を参照してください。 -
login
public boolean login() throws LoginException
login
メソッドは、ユーザーを認証し、認証情報のコンテナにコールバックすることでそのユーザーのプリンシパルを作成しようと試みます。複数のLoginModuleが複数の認証プロバイダの一部として構成されている場合、このメソッドはLoginModuleごとにその構成の順序で呼び出されます。ログインが成功したかどうか(すなわち、プリンシパルが作成されたかどうか)についての情報は、LoginModuleごとに格納されます。 -
commit
public boolean commit() throws LoginException
commit
メソッドは、login
メソッドで作成されたプリンシパルをサブジェクトに追加しようと試みます。このメソッドも、構成されている認証プロバイダの一部として構成されているLoginModuleごとに呼び出され、順番に実行されます。コミットが成功したかどうかの情報は、LoginModuleごとに格納されます。 -
abort
public boolean abort() throws LoginException
abort
メソッドは、構成されている認証プロバイダの一部として構成されているLoginModuleのコミットが失敗した(つまり、関連するREQUIRED
、REQUISITE
、SUFFICIENT
、およびOPTIONAL
LoginModuleが成功しなかった)場合にLoginModuleごとに呼び出されます。abort
メソッドは、LoginModuleのプリンシパルをサブジェクトから削除し、実行されたアクションを効果的にロールバックします。LoginModuleインタフェースに関する項(http://docs.oracle.com/javase/8/docs/api/javax/security/auth/spi/LoginModule.html
)を参照してください。 -
logout
public boolean logout() throws LoginException
logout
メソッドは、ユーザーをシステムからログアウトさせようとします。また、サブジェクトのリセットも行うので、関連付けられているプリンシパルは格納されなくなります。ノート:
LoginModule.logout
メソッドはWebLogic認証プロバイダまたはカスタム認証プロバイダに対して呼び出されることはありません。これは単に、いったんプリンシパルが作成されサブジェクト内に入れられると、WebLogicセキュリティ・フレームワークはこのサブジェクトのライフサイクルを制御しなくなるからです。したがって、JAAS LoginContextを作成してログインおよびサブジェクトの取得を行う、開発者によって記述されたユーザー・コードでも、LoginContext.logout
メソッドを呼び出す必要があります。ユーザー・コードが、JAASを直接使用するJavaクライアントで実行される場合、このコードにはLoginContext.logout
メソッドを呼び出すというオプションがあります。これにより、サブジェクトはクリアされます。ユーザー・コードがサーブレットで実行される場合、サーブレットはサーブレット・セッションからユーザーをログアウトすることができます。これにより、サブジェクトはクリアされます。
Java Authentication and Authorization Service (JAAS)開発者ガイド(http://docs.oracle.com/javase/8/docs/technotes/guides/security/jaas/JAASLMDevGuide.html
)およびLoginModuleインタフェースに関する項(http://docs.oracle.com/javase/8/docs/api/javax/security/auth/spi/LoginModule.html
)を参照してください。
LoginModuleからカスタム例外のスロー
記述したLoginModuleからカスタム例外をスローすることができます。そうすると、カスタム例外をアプリケーションおよび、実行された適切なアクションで捕捉できます。たとえば、LoginModuleからPasswordChangeRequiredException
がスローされると、その例外をアプリケーションで捕捉して、パスワードの変更が可能なページへユーザーを導くのに使用できます。
LoginModuleからカスタム例外をスローし、それをアプリケーション内で捕捉する場合には、以下のことを確認する必要があります。
-
例外を捕捉するアプリケーションがサーバー上で実行されています。(ファット・クライアントはカスタム例外を捕捉できません。)
-
サーブレットが、コンパイル時でもデプロイメント時でもカスタム例外クラスにアクセスできます。これは、必要に応じて次の方法のいずれかで行えます。
方法1 :システム・クラスパスおよびコンパイラ・クラスパスを介してカスタム例外を使えるようにする
-
LoginException
を拡張する例外クラスを記述します。 -
LoginModule
インタフェースおよびAuthenticationProvider
インタフェースを実装するクラスでカスタム例外クラスを使用します。 -
セキュリティ・プロバイダの ランタイム・クラスをコンパイルする際に、カスタム例外クラスをシステム・クラスパスおよびコンパイラ・クラスパスの両方に入れます。
-
「WebLogic MBeanMakerを使用してMBeanタイプを生成する」で説明されているようにカスタム認証プロバイダのMBeanタイプを生成します。
方法2 :アプリケーション・クラスパスを介してカスタム例外を使えるようにする
-
LoginException
を拡張する例外クラスを記述します。 -
LoginModule
インタフェースおよびAuthenticationProvider
インタフェースを実装するクラスでカスタム例外クラスを使用します。 -
カスタム例外のソースをアプリケーションのビルドのクラスパスに入れ、それをアプリケーションのJAR/WARファイルのクラスパスに含めます。
-
「WebLogic MBeanMakerを使用してMBeanタイプを生成する」で説明されているようにカスタム認証プロバイダのMBeanタイプを生成します。
-
WebLogic MBeanMakerで生成されたMJF (MBean JARファイル)にカスタム例外クラスを追加します。
-
アプリケーションのコンパイル時にMJFを含めます。
例:サンプル認証プロバイダのランタイム・クラスの作成
例3-1は、サンプル認証プロバイダの2つのランタイム・クラスの1つであるSimpleSampleAuthenticationProviderImpl.java
クラスを示しています。このランタイム・クラスには次の実装が含まれています。
-
initialize
、getDescription
およびshutdown
というSecurityProvider
インタフェースから継承した3つのメソッド(Provider SSPIの目的についてを参照。) -
getLoginModuleConfiguration
、getAssertionModuleConfiguration
、getPrincipalValidator
、およびgetIdentityAsserter
というAuthenticationProviderV2
SSPIの4つのメソッド(「AuthenticationProviderV2 SSPIの実装」を参照)。ノート:
例3-1の太字のコードは、クラス宣言とメソッド・シグネチャを示しています。
例3-1 SimpleSampleAuthenticationProviderImpl.java
package examples.security.providers.authentication.simple; 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.spi.AuthenticationProviderV2; import weblogic.security.spi.IdentityAsserterV2; import weblogic.security.spi.PrincipalValidator; import weblogic.security.spi.SecurityServices; import weblogic.security.principal.WLSGroupImpl; import weblogic.security.principal.WLSUserImpl; public final class SimpleSampleAuthenticationProviderImpl implements AuthenticationProviderV2 { private String description; private SimpleSampleAuthenticatorDatabase database; private LoginModuleControlFlag controlFlag; public void initialize(ProviderMBean mbean, SecurityServices services) { System.out.println("SimpleSampleAuthenticationProviderImpl.initialize"); SimpleSampleAuthenticatorMBean myMBean = (SimpleSampleAuthenticatorMBean)mbean; description = myMBean.getDescription() + "\n" + myMBean.getVersion(); database = new SimpleSampleAuthenticatorDatabase(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("SimpleSampleAuthenticationProviderImpl.shutdown"); } private AppConfigurationEntry getConfiguration(HashMap options) { options.put("database", database); return new AppConfigurationEntry( "examples.security.providers.authentication.Simple.Simple.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 IdentityAsserterV2 getIdentityAsserter() { return null; } }
例3-2は、サンプル認証プロバイダの2つのランタイム・クラスの1つであるSampleLoginModuleImpl.java
クラスを示しています。このランタイム・クラスは、JAAS LoginModuleインタフェースを実装するため(「JAAS LoginModuleインタフェースの実装」を参照)、そのinitialize
、login
、commit
、abort
およびlogout
メソッドの実装を含んでいます。
ノート:
例3-2の太字のコードは、クラス宣言とメソッド・シグネチャを示しています。
例3-2 SimpleSampleLoginModuleImpl.java
package examples.security.providers.authentication.simple; 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 SimpleSampleLoginModuleImpl implements LoginModule { private Subject subject; private CallbackHandler callbackHandler; private SimpleSampleAuthenticatorDatabase database; // Determine whether this is a login or assert identity private boolean isIdentityAssertion; // Authentication status private boolean loginSucceeded; private boolean principalsInSubject; private Vector principalsForSubject = new Vector(); public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) { // only called (once!) after the constructor and before login System.out.println("SimpleSampleLoginModuleImpl.initialize"); this.subject = subject; this.callbackHandler = callbackHandler; // Check for Identity Assertion option isIdentityAssertion = "true".equalsIgnoreCase((String)options.get("IdentityAssertion")); database = (SimpleSampleAuthenticatorDatabase)options.get("database"); } public boolean login() throws LoginException { // only called (once!) after initialize System.out.println("SimpleSampleLoginModuleImpl.login"); // loginSucceeded should be false // principalsInSubject should be false 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." ); } } } else { // anonymous login - let it through? System.out.println("\tempty userName"); } loginSucceeded = true; principalsForSubject.add(new WLSUserImpl(userName)); addGroupsForSubject(userName); return loginSucceeded; } public boolean commit() throws LoginException { // only called (once!) after login // loginSucceeded should be true or false // principalsInSubject should be false // user should be null if !loginSucceeded, null or not-null otherwise // group should be null if user == null, null or not-null otherwise System.out.println("SimpleSampleLoginModule.commit"); if (loginSucceeded) { subject.getPrincipals().addAll(principalsForSubject); principalsInSubject = true; return true; } else { return false; } } public boolean abort() throws LoginException { // The abort method is called to abort the authentication process. This is // phase 2 of authentication when phase 1 fails. It is called if the // LoginContext's overall authentication failed. // loginSucceeded should be true or false // user should be null if !loginSucceeded, otherwise null or not-null // group should be null if user == null, otherwise null or not-null // principalsInSubject should be false if user is null, otherwise true // or false System.out.println("SimpleSampleLoginModule.abort"); if (principalsInSubject) { subject.getPrincipals().removeAll(principalsForSubject); principalsInSubject = false; } return true; } public boolean logout() throws LoginException { // should never be called System.out.println("SimpleSampleLoginModule.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; } }
管理コンソールによるカスタム認証プロバイダの構成
カスタム認証プロバイダを構成するということは、そのカスタム認証プロバイダをセキュリティ・レルムに追加するということです。追加されたカスタム認証プロバイダには、認証サービスを必要とするアプリケーションからアクセスできます。
カスタム・セキュリティ・プロバイダの構成は管理タスクですが、カスタム・セキュリティ・プロバイダの開発者が行うこともできます。この項では、カスタム認証プロバイダの構成担当者にとって重要な情報を提供します。
-
ノート:
WebLogic Server管理コンソールを使用してカスタム認証プロバイダを構成するステップは、Oracle WebLogic Serverセキュリティの管理のWebLogicセキュリティ・プロバイダの構成で説明されています。
ユーザー・ロックアウトの管理
カスタム認証プロバイダを使用する一環として、ユーザー・ロックアウトを構成および管理する方法を検討する必要があります。以下の2つの選択肢があります。
レルム・ワイドのユーザー・ロックアウト・マネージャの使用
WebLogicセキュリティ・フレームワークは、WebLogicセキュリティ・フレームワークと直接連携してユーザー・ロックアウトを管理するレルム・ワイドのユーザー・ロックアウト・マネージャを提供します。
ノート:
レルム・ワイドのユーザー・ロックアウト・マネージャとWebLogic Server PasswordValidatorMBean
(レルム・レベル)の両方をアクティブにできます。Oracle WebLogic Server Java APIリファレンスを参照してください。
レルム・ワイドのユーザー・ロックアウト・マネージャを使用する場合、それをカスタム認証プロバイダと連携させるためには、WebLogic Server管理コンソールを使用して次のことを行います。
-
ユーザー・ロック・アウトを有効にします。(デフォルトで有効にする必要があります。)
-
必要に応じてユーザー・ロックアウトのパラメータを修正します。
ノート:
ユーザー・ロックアウト・マネージャへの変更は、サーバーを再起動するまで有効になりません。WebLogic Server管理コンソールを使用して前述のタスクを実行する手順は、Oracle WebLogic Serverセキュリティの管理のユーザー・アカウントの保護で説明されています。
独自のユーザー・ロックアウト・マネージャの実装
カスタム認証プロバイダの一部として独自のユーザー・ロックアウト・マネージャを実装する場合は、次の手順を行う必要があります。
-
レルム・ワイドのユーザー・ロックアウト・マネージャを無効にして二重ロックアウトを防止します。(WebLogic Server管理コンソールを使用して新しいセキュリティ・レルムを作成すると、ユーザー・ロックアウト・マネージャが必ず作成されます。)このタスクを実行する手順は、Oracle WebLogic Serverセキュリティの管理のユーザー・アカウントの保護で説明されています。
-
WebLogicセキュリティ・フレームワークのレルム・ワイドの実装からは何も借用できないので、以下のタスクを行うことも必要です。
-
ユーザー・ロックアウト・マネージャの実装を提供します。ユーザー・ロックアウト・マネージャに関して、セキュリティ・サービス・プロバイダ・インタフェース(SSPI)は提供されません。
-
ユーザー・ロックアウト・マネージャの管理に使用できるMBeanを変更します。
-
管理コンソールからユーザー・ロックアウト・マネージャを管理する場合、コンソール拡張を使用してWebLogic Server管理コンソールにユーザー・ロックアウト・マネージャを組み入れます。
-
認証プロバイダの順序の指定
「LoginModuleとマルチパート認証」で説明されているように、認証プロセスの結果は、複数の認証プロバイダ(その結果としてLoginModule)の構成順序によって影響を受けます。
認証プロバイダは任意の順序で構成できます。ただし、構成済の認証プロバイダの順序を変更する必要がある場合は、Oracle WebLogic Serverセキュリティの管理の認証プロバイダの順序の変更で説明されているステップに従ってください。