前回のログイン時刻を表示するポートレットの作成方法

このクックブックでは、前回のログイン時刻を表示するポートレットの作成方法をご紹介します。
具体的には、以下の3つの情報を表示するポートレットを作成します。

  • 前回のログイン時刻
  • 前回ログイン時のIPアドレス
  • 前回ログイン時のUserAgent

完成イメージ

完成サンプル

e builder プロジェクト : im_cookbook_116892_last_login_date.zip
imm ファイル : im_cookbook_116892_last_login_date-1.0.0.imm

レシピ

  1. 認証リスナの作成
  2. 認証リスナの設定
  3. ポートレット画面の作成
  4. ポートレットの追加

1. 認証リスナの作成

まず、認証リスナを作成します。
以下の3つの情報をアカウント属性として保存します。

  • 前回のログイン時刻
  • 前回ログイン時のIPアドレス
  • 前回ログイン時のUserAgent
src/main/java/jp/co/intra_mart/cookbook/im_cookbook_116892/LoginDateRegisterCertificationListener.java
public class LoginDateRegisterCertificationListener implements UserCertificationListener {

    private static final String ATTR_NAME_PREFIX = "im_cookbook_116892.";

    public static final String ATTR_NAME_4_LAST_LOGIN_DATE = ATTR_NAME_PREFIX + "LAST_LOGIN_DATE";

    public static final String ATTR_NAME_4_LAST_LOGIN_IP_ADDRESS = ATTR_NAME_PREFIX + "LAST_LOGIN_IP_ADDRESS";

    public static final String ATTR_NAME_4_LAST_LOGIN_USER_AGENT = ATTR_NAME_PREFIX + "LAST_LOGIN_USER_AGENT";

    public static final String ATTR_NAME_4_BEFORE_LAST_LOGIN_DATE = ATTR_NAME_PREFIX + "BEFORE_LAST_LOGIN_DATE";

    public static final String ATTR_NAME_4_BEFORE_LAST_LOGIN_IP_ADDRESS = ATTR_NAME_PREFIX + "BEFORE_LAST_LOGIN_IP_ADDRESS";

    public static final String ATTR_NAME_4_BEFORE_LAST_LOGIN_USER_AGENT = ATTR_NAME_PREFIX + "BEFORE_LAST_LOGIN_USER_AGENT";

    private static final String HEADER_NAME_4_AUTHORIZATION = "Authorization";

    private static final String HEADER_NAME_4_USER_AGENT = "User-Agent";

    private static final Logger LOGGER = Logger.getLogger();

    @Override
    public void doCertification(final CertificationStatus status, final LoginInfo loginInfo, final AccountInfo user, final HttpServletRequest request, final HttpServletResponse response) {

        if (status == CertificationStatus.CR_OK) {

            final String authorizationHeader = request.getHeader(HEADER_NAME_4_AUTHORIZATION);
            if (authorizationHeader != null) {
                LOGGER.trace("Ignore Basic Authentication etc. - authorizationHeader: {}", authorizationHeader);
                return;
            }

            // Change data
            changeLastDataWithBeforeLastData(user.getUserCd());

            final AccountInfoManager accountInfoManager = new AccountInfoManager();

            try {
                // Set : last login date
                final AccountContext accountContext = Contexts.get(AccountContext.class);
                final Date loginTime = accountContext.getLoginTime();
                accountInfoManager.setAttribute(user.getUserCd(), ATTR_NAME_4_LAST_LOGIN_DATE, ISO8601DateFormat.format(loginTime));

                // Set : ip address
                String ipAddress = "Unknown";
                final InetAddress remoteAddress = InetAddressDetector.getRemoteAddress(request);
                if (remoteAddress != null) {
                    ipAddress = remoteAddress.getHostAddress();
                }
                accountInfoManager.setAttribute(user.getUserCd(), ATTR_NAME_4_LAST_LOGIN_IP_ADDRESS, ipAddress);

                // Set : user agent
                String userAgent = "Unknown";
                if (request.getHeader(HEADER_NAME_4_USER_AGENT) != null) {
                    userAgent = request.getHeader(HEADER_NAME_4_USER_AGENT);
                }
                accountInfoManager.setAttribute(user.getUserCd(), ATTR_NAME_4_LAST_LOGIN_USER_AGENT, userAgent);

                if (LOGGER.isTraceEnabled()) {
                    LOGGER.trace("----------------------------------------");
                    LOGGER.trace("userCd: {}", user.getUserCd());
                    LOGGER.trace("loginTime: {}", loginTime);
                    LOGGER.trace("ipAddress: {}", ipAddress);
                    LOGGER.trace("userAgent: {}", userAgent);
                    LOGGER.trace("----------------------------------------");
                }

            } catch (UnknownHostException | InetAddressDetectException | AdminException e) {
                throw new IllegalStateException(e.getLocalizedMessage(), e);
            }

        } else {
            LOGGER.trace("Certification failed. (status: {})", status);
        }

    }

    private void changeLastDataWithBeforeLastData(final String userCd) {

        try {
            final AccountInfoManager accountInfoManager = new AccountInfoManager();

            final String lastLoginDate = accountInfoManager.getAttribute(userCd, ATTR_NAME_4_LAST_LOGIN_DATE);
            if (lastLoginDate != null) {
                accountInfoManager.setAttribute(userCd, ATTR_NAME_4_BEFORE_LAST_LOGIN_DATE, lastLoginDate);
            }

            final String lastLoginIpAddress = accountInfoManager.getAttribute(userCd, ATTR_NAME_4_LAST_LOGIN_IP_ADDRESS);
            if (lastLoginIpAddress != null) {
                accountInfoManager.setAttribute(userCd, ATTR_NAME_4_BEFORE_LAST_LOGIN_IP_ADDRESS, lastLoginIpAddress);
            }

            final String userAgent = accountInfoManager.getAttribute(userCd, ATTR_NAME_4_LAST_LOGIN_USER_AGENT);
            if (userAgent != null) {
                accountInfoManager.setAttribute(userCd, ATTR_NAME_4_BEFORE_LAST_LOGIN_USER_AGENT, userAgent);
            }

        } catch (final AdminException e) {
            throw new IllegalStateException(e.getLocalizedMessage(), e);
        }

    }

}
行数 説明
26 認証が成功した場合にだけ、前回ログイン時の情報を更新します。
28 Basic認証など Authrorazationヘッダを利用した認証処理を除外しています。これは、ユーザが故意にログイン処理を行っていない場合、前回ログイン時の情報を更新しないようにするための処理です。(例:intra-mart Accel Collaboration の CalDAV連携によるBasic認証処理)
35 前回ログイン時の情報を別のアカウント属性名で保存し直しています。
43 「今回のログイン時刻」をアカウント属性に保存しています。
47 InetAddressDetector を利用して、リクエストからリモートホストのIPアドレスを取得しています。これにより、ロードバランサやプロキシサーバを使用している環境でもIPアドレスを取得することが可能です。詳しくは、 APIドキュメント、および、IPアドレス取得元設定 を参照してください。
51 「今回ログイン時のIPアドレス」をアカウント属性に保存しています。
58 「今回ログイン時のUserAgent」をアカウント属性に保存しています。

認証リスナの詳細は、以下を参照してください。

2. 認証リスナの設定

次に、認証リスナの設定を行います。

認証リスナは、plugin.xml にて設定を行います。
詳細は、認証仕様書を参照してください。

src/main/plugin/im_cookbook_116892_last_login_date_1.0.0/plugin.xml
<?xml version="1.0" encoding="UTF-8"?>
<plugin>
  <extension point="jp.co.intra_mart.security.user.certification">
    <certification
      name="LoginDateRegister"
      id="im_cookbook_116892.LoginDateRegister"
      version="1.0.0"
      rank="100">
      <certification-listener>
        <listener-class>jp.co.intra_mart.cookbook.im_cookbook_116892.LoginDateRegisterCertificationListener</listener-class>
      </certification-listener>
    </certification>
  </extension>

  <extension point="jp.co.intra_mart.security.user.force_login">
    <certification
      name="LoginDateRegister.forceLogin"
      id="im_cookbook_116892.LoginDateRegister.forceLogin"
      version="1.0.0"
      rank="100">
      <certification-listener>
        <listener-class>jp.co.intra_mart.cookbook.im_cookbook_116892.LoginDateRegisterCertificationListener</listener-class>
      </certification-listener>
    </certification>
  </extension>

</plugin>
行数 説明
3 認証リスナの拡張ポイント「jp.co.intra_mart.security.user.certification」に対して設定します。
10 手順1で作成した認証リスナ「LoginDateRegisterCertificationListener」クラスの完全修飾クラス名(FQCN)を設定します。
15 強制ログイン用認証リスナの拡張ポイント「jp.co.intra_mart.security.user.force_login」に対して設定します。
22 強制ログインの場合も、通常の認証と同じ処理を行うため、10行目と同じクラスを設定します。
  • なお、強制ログイン用認証リスナは、2015 Winter(Lydia) より利用可能です。
    2015 Summer(Karen) 以前のバージョンでは、強制ログイン時に認証リスナは実行されません。

3. ポートレット画面の作成

認証リスナでアカウント属性に保存した前回ログイン時の情報を取得します。
情報の取得には、 AccountInfoManager#getAttribute() を利用しています。
詳しくは、以下のソースを参照してください。

スクリプト開発モデルの場合
src/main/jssp/src/im_cookbook_116892_last_login_date/show_last_login_date.html
<div class="imui-form-container">

<table class="imui-form">
  <tr>
    <th><label>Last login date</label></th>
    <td><imart type="string" value=$loginDate /></td>
  </tr>
  <tr>
    <th><label>IP address</label></th>
    <td><imart type="string" value=$loginIpAddress /></td>
  </tr>
  <tr>
    <th><label>User Agent</label></th>
    <td><imart type="string" value=$loginUserAgent /></td>
  </tr>
</table>

</div>
src/main/jssp/src/im_cookbook_116892_last_login_date/show_last_login_date.js
var $loginDate;
var $loginIpAddress;
var $loginUserAgent;

var IS_NOT_SET_YET = "-";
var PREFIX = "im_cookbook_116892.";

function init(request){
    var userCd = Contexts.getAccountContext().userCd;
    var manager = new AccountInfoManager();

    $loginDate = manager.getAttribute(userCd, PREFIX + "BEFORE_LAST_LOGIN_DATE", IS_NOT_SET_YET).data;
    if($loginDate != IS_NOT_SET_YET){
        var javaDate = Packages.jp.co.intra_mart.common.aid.jdk.java.text.ISO8601DateFormat.parse($loginDate);
        $loginDate = AccountDateTimeFormatter.format(new Date(javaDate.getTime()),
                                                     AccountDateTimeFormatter.IM_DATETIME_FORMAT_DATE_STANDARD,
                                                     AccountDateTimeFormatter.IM_DATETIME_FORMAT_TIME_TIMESTAMP);
    }

    $loginIpAddress = manager.getAttribute(userCd, PREFIX + "BEFORE_LAST_LOGIN_IP_ADDRESS", IS_NOT_SET_YET).data;
    $loginUserAgent = manager.getAttribute(userCd, PREFIX + "BEFORE_LAST_LOGIN_USER_AGENT", IS_NOT_SET_YET).data;

}

Java開発モデルの場合
src/main/webapp/im_cookbook/116892/show_last_login_date.jsp
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>

<%@ page import="jp.co.intra_mart.foundation.context.Contexts" %>
<%@ page import="jp.co.intra_mart.foundation.context.model.AccountContext" %>
<%@ page import="jp.co.intra_mart.foundation.admin.account.AccountInfoManager" %>
<%@ page import="jp.co.intra_mart.common.aid.jdk.java.text.ISO8601DateFormat" %>
<%@ page import="jp.co.intra_mart.foundation.i18n.datetime.format.AccountDateTimeFormatter" %>
<%@ page import="jp.co.intra_mart.foundation.i18n.datetime.format.DateTimeFormatIds" %>

<%@ page import="jp.co.intra_mart.cookbook.im_cookbook_116892.LoginDateRegisterCertificationListener" %>

<%
String IS_NOT_SET_YET = "-";

String userCd = Contexts.get(AccountContext.class).getUserCd();
AccountInfoManager accountInfoManager = new AccountInfoManager();

String loginDate = accountInfoManager.getAttribute(userCd,
                                                   LoginDateRegisterCertificationListener.ATTR_NAME_4_BEFORE_LAST_LOGIN_DATE,
                                                   IS_NOT_SET_YET);
if (!IS_NOT_SET_YET.equals(loginDate)) {
    loginDate = AccountDateTimeFormatter.format(ISO8601DateFormat.parse(loginDate),
                                                DateTimeFormatIds.IM_DATETIME_FORMAT_DATE_STANDARD,
                                                DateTimeFormatIds.IM_DATETIME_FORMAT_TIME_TIMESTAMP);
}

String loginIpAddress = accountInfoManager.getAttribute(userCd,
                                                        LoginDateRegisterCertificationListener.ATTR_NAME_4_BEFORE_LAST_LOGIN_IP_ADDRESS,
                                                        IS_NOT_SET_YET);

String loginUserAgent = accountInfoManager.getAttribute(userCd,
                                                        LoginDateRegisterCertificationListener.ATTR_NAME_4_BEFORE_LAST_LOGIN_USER_AGENT,
                                                        IS_NOT_SET_YET);
%>

<div class="imui-form-container">

  <table class="imui-form">
    <tr>
      <th><label>Last login date</label></th>
      <td><%=loginDate %></td>
    </tr>
    <tr>
      <th><label>IP address</label></th>
      <td><%=loginIpAddress %></td>
    </tr>
    <tr>
      <th><label>User Agent</label></th>
      <td><%=loginUserAgent %></td>
    </tr>
  </table>

</div>

4. ポートレットの追加

最後にポートレットを追加します。

手順3で作成した「スクリプト開発ポートレット」または「Servlet Jsp ポートレット」を登録します。ポートレットのアクセス権(表示)を設定し、グループポータル、または、ユーザポータルにポートレットを追加してください。

  • 「スクリプト開発ポートレット」として登録する場合
    • ページパス : im_cookbook_116892_last_login_date/show_last_login_date
  • 「Servlet Jsp ポートレット」として登録する場合
    • URL : im_cookbook/116892/show_last_login_date.jsp

ポートレット追加方法の詳細は、以下を参照してください。

まとめ

このクックブックでは、前回のログイン時刻を表示するポートレットの作成方法をご紹介しました。
具体的には、以下の3つの情報を表示するポートレットを作成しました。

  • 前回のログイン時刻
  • 前回ログイン時のIPアドレス
  • 前回ログイン時のUserAgent

前回ログイン時の情報の保存には、「認証リスナ」を利用しました。
intra-mart Accel Platform の 認証機能は、認証リスナの他にも様々なプラグインで機能を拡張することが可能です。
お客様の用途にあわせて是非ご活用ください。