16.2. カスタムモジュール
AuthenticationManager は Subject プリンシパルセットの特定の使用パターンを必要とします。AuthenticationManager と動作するログインモジュールを書くには、JAAS サブジェクトクラスの情報ストレージ機能と、これらの機能の想定される使用方法を理解する必要があります。
LoginModule 実装を紹介します。
Subject に関連するセキュリティー情報を取得できます。
java.util.Set getPrincipals()
java.util.Set getPrincipals(java.lang.Class c)
java.util.Set getPrivateCredentials()
java.util.Set getPrivateCredentials(java.lang.Class c)
java.util.Set getPublicCredentials()
java.util.Set getPublicCredentials(java.lang.Class c)
Subject アイデンティティーおよびロールに対し、EAP は getPrincipals() および getPrincipals(java.lang.Class) から取得したプリンシパルセットを選択します。使用パターンは次のとおりです。
- ユーザーアイデンティティー (例: ユーザー名、従業員 ID など) は
java.security.PrincipalオブジェクトとしてSubjectPrincipalsセットに保存されます。ユーザーアイデンティティーを示すPrincipal実装は、プリンシパルの名前に基づいた比較および等価が必要になります。適切な実装はorg.jboss.security.SimplePrincipalクラスとして使用可能です。必要な場合は、他のPrincipalインスタンスをSubjectPrincipalsに追加できます。 - 割り当てられたユーザーロールも
Principalsセットに保存され、java.security.acl.Groupインスタンスを使用して名前付きロールセットにグループ化されます。Groupインターフェースはjava.security.Principalのサブインスタンスで、PrincipalやGroupのコレクションを定義します。 - 任意の数のロールセットを
Subjectに割り当てできます。
- EAP セキュリティーフレームワークは、
RolesおよびCallerPrincipalという名前の 2 つのロールセットを使用します。Rolesグループは、Subjectが認証されたアプリケーションドメインで知られる名前付きロールのPrincipalのコレクションです。このロールセットは、現在の呼び出し側が名前付きアプリケーションドメインロールに属するかどうかを確認するために EJB が使用できるEJBContext.isCallerInRole(String)などのメソッドによって使用されます。メソッドパーミッションチェックを実行するセキュリティーインターセプターロジックもこのロールセットを使用します。CallerPrincipalGroupは、アプリケーションドメインのユーザーに割り当てられた単一のPrincipalアイデンティティーで構成されます。EJBContext.getCallerPrincipal()メソッドはCallerPrincipalを使用して、アプリケーションドメインが操作環境アイデンティティーからアプリケーションに適したユーザーアイデンティティーへマップできるようにします。SubjectにCallerPrincipalGroupがない場合、アプリケーションアイデンティティーは操作環境アイデンティティーと同じになります。
16.2.1. サブジェクト使用パターンのサポート リンクのコピーリンクがクリップボードにコピーされました!
Subject 使用パターンを正しく簡単に実装するため、適切に Subject を使用できるようにするテンプレートパターンを認証された Subject に追加するログインモジュールが EAP に含まれています。
2 つのログインモジュールでより汎用的なのが org.jboss.security.auth.spi.AbstractServerLoginModule クラスです。
javax.security.auth.spi.LoginModule の実装を提供し、操作環境セキュリティーインフラストラクチャー固有の主要タスクに対して抽象メソッドを提供します。このクラスの主な詳細は、例16.20「AbstractServerLoginModule クラスの一部」を参照してください。JavaDoc のコメントでサブクラスの役割が説明されています。
重要
loginOk インスタンス変数が極めて重要になります。ログインに成功した場合はこれを true に設定する必要があります。ログインに失敗した場合は、ログインメソッドをオーバーライドするサブクラスによって false に設定する必要があります。この変数が適切に設定されないと、コミットメソッドは適切にサブジェクトを更新しません。
例16.20 AbstractServerLoginModule クラスの一部
package org.jboss.security.auth.spi;
/**
* This class implements the common functionality required for a JAAS
* server-side LoginModule and implements the PicketBox standard
* Subject usage pattern of storing identities and roles. Subclass
* this module to create your own custom LoginModule and override the
* login(), getRoleSets(), and getIdentity() methods.
*/
public abstract class AbstractServerLoginModule
implements javax.security.auth.spi.LoginModule
{
protected Subject subject;
protected CallbackHandler callbackHandler;
protected Map sharedState;
protected Map options;
protected Logger log;
/** Flag indicating if the shared credential should be used */
protected boolean useFirstPass;
/**
* Flag indicating if the login phase succeeded. Subclasses that
* override the login method must set this to true on successful
* completion of login
*/
protected boolean loginOk;
// ...
/**
* Initialize the login module. This stores the subject,
* callbackHandler and sharedState and options for the login
* session. Subclasses should override if they need to process
* their own options. A call to super.initialize(...) must be
* made in the case of an override.
*
* <p>
* The options are checked for the <em>password-stacking</em> parameter.
* If this is set to "useFirstPass", the login identity will be taken from the
* <code>javax.security.auth.login.name</code> value of the sharedState map,
* and the proof of identity from the
* <code>javax.security.auth.login.password</code> value of the sharedState map.
*
* @param subject the Subject to update after a successful login.
* @param callbackHandler the CallbackHandler that will be used to obtain the
* the user identity and credentials.
* @param sharedState a Map shared between all configured login module instances
* @param options the parameters passed to the login module.
*/
public void initialize(Subject subject,
CallbackHandler callbackHandler,
Map sharedState,
Map options)
{
// ...
}
/**
* Looks for javax.security.auth.login.name and
* javax.security.auth.login.password values in the sharedState
* map if the useFirstPass option was true and returns true if
* they exist. If they do not or are null this method returns
* false.
* Note that subclasses that override the login method
* must set the loginOk var to true if the login succeeds in
* order for the commit phase to populate the Subject. This
* implementation sets loginOk to true if the login() method
* returns true, otherwise, it sets loginOk to false.
*/
public boolean login()
throws LoginException
{
// ...
}
/**
* Overridden by subclasses to return the Principal that
* corresponds to the user primary identity.
*/
abstract protected Principal getIdentity();
/**
* Overridden by subclasses to return the Groups that correspond
* to the role sets assigned to the user. Subclasses should
* create at least a Group named "Roles" that contains the roles
* assigned to the user. A second common group is
* "CallerPrincipal," which provides the application identity of
* the user rather than the security domain identity.
*
* @return Group[] containing the sets of roles
*/
abstract protected Group[] getRoleSets() throws LoginException;
}
カスタムログインモジュールに適している 2 つ目の抽象ベースログインモジュールは org.jboss.security.auth.spi.UsernamePasswordLoginModule です。
char[] パスワードを認証クレデンシャルとすることで、カスタムログインモジュールの実装をさらに簡素化します。また、このログインモジュールは、匿名ユーザー (null のユーザー名とパスワードによって示される) をロールを持たないプリンシパルへマップすることをサポートします。クラスの主な詳細は以下を参照してください。JavaDoc のコメントでサブクラスの役割が説明されています。
例16.21 UsernamePasswordLoginModule クラスの一部
package org.jboss.security.auth.spi;
/**
* An abstract subclass of AbstractServerLoginModule that imposes a
* an identity == String username, credentials == String password
* view on the login process. Subclasses override the
* getUsersPassword() and getUsersRoles() methods to return the
* expected password and roles for the user.
*/
public abstract class UsernamePasswordLoginModule
extends AbstractServerLoginModule
{
/** The login identity */
private Principal identity;
/** The proof of login identity */
private char[] credential;
/** The principal to use when a null username and password are seen */
private Principal unauthenticatedIdentity;
/**
* The message digest algorithm used to hash passwords. If null then
* plain passwords will be used. */
private String hashAlgorithm = null;
/**
* The name of the charset/encoding to use when converting the
* password String to a byte array. Default is the platform's
* default encoding.
*/
private String hashCharset = null;
/** The string encoding format to use. Defaults to base64. */
private String hashEncoding = null;
// ...
/**
* Override the superclass method to look for an
* unauthenticatedIdentity property. This method first invokes
* the super version.
*
* @param options,
* @option unauthenticatedIdentity: the name of the principal to
* assign and authenticate when a null username and password are
* seen.
*/
public void initialize(Subject subject,
CallbackHandler callbackHandler,
Map sharedState,
Map options)
{
super.initialize(subject, callbackHandler, sharedState,
options);
// Check for unauthenticatedIdentity option.
Object option = options.get("unauthenticatedIdentity");
String name = (String) option;
if (name != null) {
unauthenticatedIdentity = new SimplePrincipal(name);
}
}
// ...
/**
* A hook that allows subclasses to change the validation of the
* input password against the expected password. This version
* checks that neither inputPassword or expectedPassword are null
* and that inputPassword.equals(expectedPassword) is true;
*
* @return true if the inputPassword is valid, false otherwise.
*/
protected boolean validatePassword(String inputPassword,
String expectedPassword)
{
if (inputPassword == null || expectedPassword == null) {
return false;
}
return inputPassword.equals(expectedPassword);
}
/**
* Get the expected password for the current username available
* via the getUsername() method. This is called from within the
* login() method after the CallbackHandler has returned the
* username and candidate password.
*
* @return the valid password String
*/
abstract protected String getUsersPassword()
throws LoginException;
}
文字別ベースのユーザー名とクレデンシャルが、作成中のカスタムログインモジュールの認証技術で使用できるかどうかを基に AbstractServerLoginModule と UsernamePasswordLoginModule のどちらをサブクラス化するかを決定します。文字別ベースのセマンティックが有効な場合は UsernamePasswordLoginModule をサブクラス化し、その他の場合は AbstractServerLoginModule をサブクラスします。
カスタムログインモジュールが実行する手順は、選択するベースログインモジュールクラスによって異なります。セキュリティーインフラストラクチャーと統合するカスタムログインモジュールを作成する場合は、EAP セキュリティーマネージャーが想定する形式の認証された Principal 情報がログインモジュールによって提供されるようにするため、最初に AbstractServerLoginModule または UsernamePasswordLoginModule をサブクラス化します。
AbstractServerLoginModule をサブクラス化する場合は、以下をオーバーライドする必要があります。
void initialize(Subject, CallbackHandler, Map, Map): 解析するカスタムオプションがある場合。boolean login(): 認証を行うため。ログインに成功した場合は必ずloginOkインスタンス変数を true に設定します。失敗した場合は false に設定します。Principal getIdentity():log()手順によって認証されたユーザーのPrincipalオブジェクトを返します。Group[] getRoleSets(): 最低でも、login()の間に認証されたPrincipalへ割り当てられたロールが含まれるRolesという名前のGroupを返します。次に一般的なGroupの名前はCallerPrincipalで、セキュリティードメインアイデンティティーではなくユーザーのアプリケーションアイデンティティーを提供します。
UsernamePasswordLoginModule をサブクラス化する場合は、以下をオーバーライドする必要があります。
void initialize(Subject, CallbackHandler, Map, Map): 解析するカスタムオプションがある場合。Group[] getRoleSets(): 最低でも、login()の間に認証されたPrincipalへ割り当てられたロールが含まれるRolesという名前のGroupを返します。次に一般的なGroupの名前はCallerPrincipalで、セキュリティードメインアイデンティティーではなくユーザーのアプリケーションアイデンティティーを提供します。String getUsersPassword():getUsername()メソッドより使用可能な現在のユーザー名の想定されるパスワードを返します。callbackhandlerがユーザー名と候補のパスワードを返した後、getUsersPassword()メソッドはlogin()内から呼び出されます。