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
オブジェクトとしてSubject
Principals
セットに保存されます。ユーザーアイデンティティーを示すPrincipal
実装は、プリンシパルの名前に基づいた比較および等価が必要になります。適切な実装はorg.jboss.security.SimplePrincipal
クラスとして使用可能です。必要な場合は、他のPrincipal
インスタンスをSubject
Principals
に追加できます。 - 割り当てられたユーザーロールも
Principals
セットに保存され、java.security.acl.Group
インスタンスを使用して名前付きロールセットにグループ化されます。Group
インターフェースはjava.security.Principal
のサブインスタンスで、Principal
やGroup
のコレクションを定義します。 - 任意の数のロールセットを
Subject
に割り当てできます。
- EAP セキュリティーフレームワークは、
Roles
およびCallerPrincipal
という名前の 2 つのロールセットを使用します。Roles
グループは、Subject
が認証されたアプリケーションドメインで知られる名前付きロールのPrincipal
のコレクションです。このロールセットは、現在の呼び出し側が名前付きアプリケーションドメインロールに属するかどうかを確認するために EJB が使用できるEJBContext.isCallerInRole(String)
などのメソッドによって使用されます。メソッドパーミッションチェックを実行するセキュリティーインターセプターロジックもこのロールセットを使用します。CallerPrincipal
Group
は、アプリケーションドメインのユーザーに割り当てられた単一のPrincipal
アイデンティティーで構成されます。EJBContext.getCallerPrincipal()
メソッドはCallerPrincipal
を使用して、アプリケーションドメインが操作環境アイデンティティーからアプリケーションに適したユーザーアイデンティティーへマップできるようにします。Subject
にCallerPrincipal
Group
がない場合、アプリケーションアイデンティティーは操作環境アイデンティティーと同じになります。
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()
内から呼び出されます。