5.13.3 カスタムログインモジュールの実装例

カスタムログインモジュールおよびPrincipalオブジェクトの実装例を示します。

カスタムログインモジュールの実装例では,最初にセッションフェイルオーバ機能を使用しない場合の例を示します。セッションフェイルオーバ機能を使用する場合の例は,セッションフェイルオーバ機能を使用しない場合と異なる部分だけ示します。

<この項の構成>
(1) セッションフェイルオーバ機能を使用しない場合の実装例
(2) セッションフェイルオーバ機能を使用する場合の実装例
(3) Principalオブジェクトの実装例

(1) セッションフェイルオーバ機能を使用しない場合の実装例

セッションフェイルオーバ機能を使用しない場合のカスタムログインモジュールの実装例を示します。

/**
* LoginModuleの実装クラスは,LoginModuleインタフェースを継承して作成します。
*
*/
public class ExampleLoginModule implements LoginModule
{
   // initialize()メソッドに渡ってきたパラメタの値を各メソッドで参照するために使用されます。
   private Subject subject;
   private CallbackHandler callbackHandler;
   private Map sharedState;
   private Map options;


   // sharedStateからユーザIDおよびパスワードの値を取り出すときの名称を定義します。
   // “simple.login.username”, "simple.login.password";は,統合ユーザ管理のコンフィグレーションファイルで
   // 指定できます。
   private static final String USERNAME = "simple.login.username";
   private static final String PASSWORD = "simple.login.password";


   // 認証の判定をするユーザIDを格納します。この値は,login()で設定され,commit()で参照されます。
   private String username;


   // login()の正否を格納します。trueなら,login()が成功しており,falseならlogin()が失敗しています。
   // この値は,login()で設定されcommit()で参照されます。
   private boolean succeeded;


   // commit()の正否を格納します。trueなら,commit()が成功しており,falseならcommit()が失敗しています。
   // この値は,commit ()で設定されabort ()で参照されます。
   private boolean commitSucceeded;


   /**
   * initialize()メソッドでは,引数に渡ってきたパラメタをメンバ変数に覚えます。
   * また,これ以外に初期化が必要な処理があればそれを行います。
   * (本クラスインスタンス時に1回だけ呼ばれる)
   *
   */
   public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options)
   {
       this.subject = subject;
       this.callbackHandler = callbackHandler;
       this.sharedState = sharedState;
       this.options = options;
   }


   /**
   * login ()メソッドでは,認証に必要なユーザIDの取得と認証処理を行います。
   * 本例では,認証に成功した場合succeededにtrueを設定します。また,usernameに認証したユーザID
   * を格納します。
   *
   */
   public boolean login()
       throws LoginException
   {
       // SSOに対応するには,最初にsharedState内にユーザID・パスワードを取り出します。
       this.username = (String)this.sharedState.get(USERNAME);
       String password = (String)this.sharedState.get(PASSWORD);


       // sharedState内にユーザIDがない場合,CallbackHandlerを使ってユーザID/パスワードの
       // 取り出しを行います。(この例では,WebPasswordCallbackに値を設定してくれる,WebPasswordHandlerが
       // が前提になります)
       if (this.username == null || this.username.length() == 0) {
           WebPasswordCallback webpc = new WebPasswordCallback();
           webpc.setOption(WebPasswordCallback.GETPW);
           Callback callbacks[] = new Callback[] { webpc };
           try {
               this.callbackHandler.handle(callbacks);
           }
           catch (Exception ex) {
               // 例外処理を行います。
           }
           // CallbackからユーザIDとパスワードを取り出します。
           this.username = webpc.getName();
           password = webpc.getPassword();
       }

       // 認証で使用するユーザIDの有無を調べます。もし,ユーザIDがない場合は,例外を上げます。
       if (this.username == null || this.username.length() == 0) {
           throw new FailedLoginException();
       }

       // 各アプリケーションの認証処理を行います。
       // 認証した結果,問題がなければsucceededにtrueを設定します。

       /* ここに認証処理を入れる。 */

       if (!succeeded) {
           throw new FailedLoginException();
       }
       return succeeded;
   }

   /**
   * commit ()メソッドでは,認証したことを示すために,PrincipalオブジェクトをSubjectに関連づけます。
   * (SimplePrincipalは,PrincipalとSerializableのインタフェースを継承して作成されたクラスです。
   *
   */
   public boolean commit()
       throws LoginException
   {
       // PrincipalオブジェクトをSubjectに関連づけます。これにより,統合ユーザ管理で管理している
       // ログインセッション管理に参加したり,SSOの機能に対応することができます。
       this.subject.getPrincipals().add( new SimplePrincipal(this.username) );

       /* ユーザ属性があるなら,それもSubjectに関連づける処理を入れる。 */

       return this.commitSucceeded = true;
   }

   /**
   * abort ()メソッドでは,login()メソッドcommit()メソッドで障害があった場合にリカバリするために
   * 呼び出されます。
   *
   */
   public boolean abort()
       throws LoginException
   {
       if (this.commitSucceeded) {
           // Subjectに関連づけているPrincipalやユーザ属性を解放します。
           // この例ではlogout()メソッドを呼び出して解放します。
           logout();
       }
       return true;
   }

   /**
   * logout ()メソッドでは,ログアウトする場合に呼び出されます。
   * このメソッドでは,Subjectに関連づけられているPrincipalやユーザ属性を解放するために使用されます。
   *
   *
   */
   public boolean logout()
       throws LoginException
   {
       // SubjectからPrincipalやユーザ属性を削除する処理を入れます。
       // また,login()メソッドで確保したリソースがあれば,それを解放する処理を追加します。
       return true;
   }
}

(2) セッションフェイルオーバ機能を使用する場合の実装例

loginメソッドとcommitメソッドについては,セッションフェイルオーバ機能を使用しない場合と同じです。ここでは,違いのあるlogoutメソッドの実装方法を示します。

/**
* LoginModuleの実装クラスは,LoginModuleインタフェースを継承して作成します。
*/
public class ExampleLoginModule implements LoginModule
{
  :
 /**
  * logout ()メソッドでは,ログアウトする場合に呼び出されます。
  * このメソッドでは,ログイン時に取得したリソースを解放するために使用されます。
  */
 public boolean logout() throws LoginException
 {
   if (callbackHandler != null) {
       WebLogoutCallback callback = new WebLogoutCallback();
       try {
           callbackHandler.handle(new Callback[]{callback});
       } catch (Exception e) { ... }
       String uid = callback.getUserID();
       HttpSession session = callback.getSession();
       // login()メソッドで確保したリソースがあれば,それを解放する処理を追加します。
       //例えば,グローバルセッションに登録した情報を削除するなど。
   }
   return true;
 }
}

logoutメソッドでは,ログイン時に取得したリソースを解放します。セッションフェイルオーバ機能を使用する場合,SubjectやPrincipalはフェイルオーバされないことに注意してください。

(3) Principalオブジェクトの実装例

Principalオブジェクトは,java.security.Principalとjava.io.Serializableのインタフェースを継承して作成してください。Principalオブジェクトの実装例を示します。

import java.security.Principal;
import java.io.Serializable;

/**
* Principalの実装クラスは,PrincipalとSerializableインタフェースを継承して作成します。
*
*/
public class SimplePrincipal implements Principal, Serializable
{
 private String name;

 public SimplePrincipal(String name) {
   if (name == null) throw new NullPointerException();
   this.name = name;
 }
 public String getName() { return name; }
 public String toString() { return getName(); }
 public boolean equals(Object o) {
   if (o == null) return false;
   if (this == o) return true;
   if (!(o instanceof SimplePrincipal)) return false;
   SimplePrincipal rhs = (SimplePrincipal)o;
   if (getName().equals(rhs.getName())) return true;
   return false;
 }
 public int hashCode() { return getName().hashCode(); }
}