付録A.10 シングルサインオンのためのポートレット

この例では,統合ユーザ管理フレームワークの提供の機能を利用して,メールサーバにシングルサインオンするポートレットを示します。

<この項の構成>
(1) カスタムログインモジュールの作成
(2) 日立APIポートレット
(3) ポートレットを動作させるための設定

(1) カスタムログインモジュールの作成

まず,メールサーバとのシングルサインオンを実現するために,メールサーバと認証するカスタムログインモジュールを作成します。カスタムログインモジュールでは,sharedStateオブジェクトから,ユーザIDとパスワードを取得して認証します。次の二つのファイルを作成します。

ファイル1

package mail;

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

public class MailPrincipal implements Principal, Serializable
{
 private String name;

 public MailPrincipal(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 MailPrincipal)) return false;
   MailPrincipal rhs = (MailPrincipal)o;
   if (getName().equals(rhs.getName())) return true;
   return false;
 }
 public int hashCode() { return getName().hashCode(); }
}

ファイル2

package mail.login;

import com.cosminexus.admin.auth.*;

import javax.security.auth.*;
import javax.security.auth.login.*;
import javax.security.auth.callback.*;
import javax.security.auth.spi.*;
import java.text.*;
import java.util.*;
import javax.mail.*;
import java.util.Properties;

import mail.MailPrincipal;

public class MailLoginModule implements LoginModule {
   private Subject subject;
   private CallbackHandler callbackHandler;
   private Map sharedState;
   private Map options;

   private static final String USER_HOST_OPT = "mail.login.host";
   private static final String USER_PROV_OPT = "mail.login.provider";

   private static final String USERNAME = "mail.login.username";
   private static final String PASSWORD = "mail.login.password";

   private static final String DEFAULT_PROV = "imap";

   private static final String MSG_USERNOTFOUND = "user not found!";
   private static final String MSG_NOHOSTOPT = "not host option!";

   private boolean commitSucceeded = false;

   private Store store;
   private String username;

   public void initialize(
       Subject subject,
       CallbackHandler callbackHandler,
       Map sharedState,
       Map options)
   {
       this.subject = subject;
       this.callbackHandler = callbackHandler;
       this.sharedState = sharedState;
       this.options = options;
   }

   public boolean login()
       throws LoginException
   {
       // get user-name & password
       this.username = (String)this.sharedState.get(USERNAME);
       if (username == null) throw new LoginException(MSG_USERNOTFOUND);
       String password = (String)this.sharedState.get(PASSWORD);

       // option check
       String host = (String)this.options.get(USER_HOST_OPT);
       if (host == null || host.length()==0) throw new LoginException(MSG_NOHOSTOPT);

       String provider = (String)this.options.get(USER_PROV_OPT);
       if (provider == null ) provider = DEFAULT_PROV;

       // login
       Properties props = new Properties();
       Session session = Session.getInstance(props, null);
       try {
           store = session.getStore(provider);
           store.connect(host, username, password);
       } catch(Exception ex) {
           throw new LoginException(ex.toString());
       }
       return true;
   }

   public boolean commit() throws LoginException {
       this.subject.getPrincipals().add( new MailPrincipal(this.username) );
       this.subject.getPublicCredentials().add( this.store );
       return this.commitSucceeded = true;
   }

   public boolean abort() throws LoginException {
       try {
           if (store != null) store.close();
       } catch(MessagingException ex) {
           // Disregard
       }
       if (this.commitSucceeded) {
           logout();
       }
       return true;
   }

   public boolean logout() throws LoginException {
       try {
           if (store != null) store.close();
       } catch(MessagingException ex) {
           // Disregard
       }
       this.commitSucceeded = false;
       return true;
   }
}

(2) 日立APIポートレット

次に,メール情報を表示する日立APIポートレットを作成します。このポートレットでは,ログインに成功したあと,subjectからメール情報を取得しています。メール情報を表示するポートレットのサンプルコードを次に示します。

<%@ page contentType="text/html; charset=Shift_JIS" %>
<%@ taglib uri="http://cosminexus.com/admin/auth/uatags" prefix="ua" %>
<%@ page import="com.cosminexus.admin.auth.*" %>
<%@ page import="com.cosminexus.admin.auth.sso.callback.*" %>
<%@ page import="javax.security.auth.*" %>
<%@ page import="javax.security.auth.login.*" %>
<%@ page import="java.security.*" %>
<%@ page import="javax.mail.*" %>
<%@ page import="java.util.Properties" %>

<%
 LoginContext lc = null;
 try {
   WebSSOHandler h =
     new WebSSOHandler(request, response, null);
   lc = new LoginContext("Mail", h);
   lc.login();
 }
 catch (LoginException e) {
   out.println("Mailへのログインに失敗しました。");
   return;
 }
 catch (Exception e) {
   out.println("Mailへのログインで障害が発生しました。");
   return;
 }

 try {
     Subject subject = lc.getSubject();
     Principal principal = (Principal)subject.getPrincipals().
       iterator().next();
     String mailid = principal.getName();
     Store store =
       (Store)subject.getPublicCredentials().iterator().next();
     out.println("メールID="+mailid+"<br />");
     Folder folder = null;
     try {
         folder = store.getFolder("INBOX");
         folder.open(Folder.READ_ONLY);

         Message msg[] = folder.getMessages();
         out.println("メール総数="+msg.length+"<br />");
         out.println("<br />");
         if (msg.length == 0) {
             out.println("メールはありません<br />");
         }
         else {
             out.println("メール一覧<br />");
             out.println("<table border>");
             {
                 // 先頭行
                 out.println("<tr>");
                 out.println(
                    "<th><font size=¥"4¥">項番</th>"
                   +"<th><font size=¥"4¥">送信者</th>"
                   +"<th><font size=¥"4¥">メールタイトル</th>");
                 out.println("</tr>");

                 // メールのタイトル表示
                 int loop = msg.length>10 ? 10 : msg.length;
                 for (int i = 0; i < loop ; ++i) {
                     out.println("<tr>");
                     out.print("<td align=¥"right¥">"+(i+1)+"</td>");
                     Address[] addrs = msg[i].getFrom();
                     String requestName = (addrs != null && addrs.length > 0) ?
                       addrs[0].toString() : "不明";
                     out.print("<td>"+requestName+"</td>");
                     out.print("<td>"+msg[i].getSubject()+"</td>");
                     out.println("</tr>");
                 } // for i
             }
             out.println("</table>");
         }
     }
     catch (Exception e) {
          out.println("Mailの表示中に障害が発生しました。");
          return;
     }
     finally {
         try {
             if (folder != null) folder.close(false);
         }
         catch (MessagingException e) { e.printStackTrace(); }
     }
 }
 finally {
     try {
         lc.logout();
     }
     catch (Exception e) {
       out.println("Mailのログオフに障害が発生しました。");
       return;
     }
 }

%>
<hr />

(3) ポートレットを動作させるための設定

(2)の日立APIポートレットを動作させるための設定について説明します。

  1. (1)で作成したカスタムログインモジュールをコンパイルして,{Cosminexusインストールディレクトリ}¥manager¥modulesに格納する
    格納先のディレクトリはユーザ管理コンフィグレーションファイル(ua.conf)のcom.cosminexus.admin.auth.custom.modulesパラメタで変更できます。
  2. JAASコンフィグレーションファイル(jaas.conf)を変更する
    作成したカスタムログインモジュールとポータルのログインモジュールを関連づけるためJAASコンフィグレーションファイル変更します。変更例を次に示します。

    Portal {
     com.cosminexus.admin.auth.sso.login.WebSSOLoginModule required
       com.cosminexus.admin.auth.realm="Portal"
       com.cosminexus.admin.auth.ldap.r="0"
       com.cosminexus.admin.auth.ldap.w="1"
       ;
    };
    Mail {
     com.cosminexus.admin.auth.sso.login.WebSSOLoginModule required
       com.cosminexus.admin.auth.sso="MailLM"
       com.cosminexus.admin.auth.realm="Mail"
       mail.login.host="localhost"  /* MailLoginModuleのオプション */
       ;
    };

  3. カスタムログインモジュールを統合ユーザ管理に認識させるため,ユーザ管理コンフィグレーションファイル(ua.conf)に次の内容を追記する
    追加内容を次に示します。

    com.cosminexus.admin.auth.sso.lm.MailLM=mail.login.MailLoginModule
    com.cosminexus.admin.auth.sso.param.userid.MailLM=mail.login.username
    com.cosminexus.admin.auth.sso.param.secdat.MailLM=mail.login.password

  4. ポータルのユーザとメールのユーザのマッピングをCSVファイルに作成し,ssoimportコマンドで登録する
    登録例を次に示します。この例では,ポータルのユーザのuser1は,メールのユーザのmuser1に対応していることを示しています。ポートレットの場合,最初にポータルにログインするため,ポータルのパスワードはマッピング情報に登録する必要がありません。

    REALMNAME,USERID,SECRETDATA,PUBLICDATA,LINK_Mail
    Portal,user1,,,muser1
    Mail,muser1,pass1,,