mb_language("uni"); mb_internal_encoding("utf-8"); mb_http_input("auto"); mb_http_output("utf-8"); ?>
前回と今回ではアプリケーションに必要なものとして、認証機能の実装を紹介しています。前回の記事ではユーザ管理のためのスキーマ設計、ユーザの閲覧、追加、削除機能の実装を紹介しました。住所録アプリケーションのアイテム閲覧/登録/削除機能の実装を思い出しながら、読んでいただけたのではないでしょうか。今回はログインページ、ログアウトページを作成し、セッションによる認証機能を実装します。
住所録アプリケーションでの認証機能は以下のような動きとなります。
・ ログイン画面でログインしたユーザのみが住所録を閲覧できる
・ ログインできるユーザの情報はデータベースに登録されている
・ ログイン画面で管理者ユーザとしてログインした場合はユーザの閲覧/追加/削除ができる
(管理者ユーザとは、ユーザ名が「admin」、ユーザIDが「000001」のユーザのこと)
・ ログイン後に、何も操作せずに30分たつと再ログインが必要となる
認証機能にはセッションを利用します。ログイン画面でログインする際にXprioriに対してユーザ名とパスワードを問い合わせ、ユーザが登録済みであればユーザ名とユーザIDをセッションに設定します。この住所録アプリケーションでは、セッションにユーザ名とユーザIDが設定されている状態をログイン状態とします。各ページでは実際にセッションにユーザ名とユーザIDが登録されているか確認することで、ユーザがログイン中かどうかを判断します。
認証を行うためのクラスとしてCertificationBeanクラスを作成しました。このクラスではセッションからユーザ名とユーザIDを取得しログインしているかどうかを判断します。メンバ変数としてユーザ名、ユーザIDを持っており、それぞれset、get関数が用意されています。
● セッション内のユーザ情報の取得
セッションオブジェクトからユーザ名とユーザIDを取得し、それぞれCertificationBeanクラスのメンバ変数に設定し、その後タイムアウト期間(30分)を再設定します。セッションオブジェクトのgetAttribute関数は引数で指定した名前のセッション値を取得する関数であり、setMaxInactiveInterval関数はセッションの有効期限を設定する関数です。引数にはタイムアウト期間を秒単位で設定します。
public void getSessionInfo(HttpSession session){ //セッションからユーザ名とユーザIDを取得する this.setUsername((String)session.getAttribute("username")); this.setUserid((String)session.getAttribute("userid")); //タイムアウト期間を再設定する session.setMaxInactiveInterval(1800); } |
● ログインチェック
セッションの設定を行った後、ユーザがログインしているかどうかを、ユーザ名があるかどうかを見て判断します。ログインしていない場合はユーザ名がnullとなっているので偽を返し、そうでなければ真を返します。
public boolean checkUserExist(HttpSession session){ //セッション情報を設定する this.getSessionInfo(session); //ユーザ名がnullかどうか調べる if(this.username == null){ return false; } else{ return true; } } |
● 管理者ログインチェック
セッションの設定を行った後、ユーザが管理者としてログインしているかどうかを、ユーザ名があるかどうか、ユーザ名が「admin」かどうか、ユーザIDが「000001」かどうかの3点を見て判断します。管理者ログインでは無い場合は偽を返し、そうでなければ真を返します。
public boolean checkAdminUserExist(HttpSession session){ //セッション情報を設定する this.getSessionInfo(session); //ユーザ名がadminかつユーザIDが000001であることを調べる if(this.username != null && this.getUsername().equals("admin") && this.getUserid().equals("000001")){ return true; } else{ return false; } } |
CertificationBeanクラスの作成に加えて、前回作成したユーザ検索用のSearchUserInfoBeanクラスを拡張しました。ログイン画面で入力された情報を元に、ユーザがXprioriに登録済みかどうかを調べるための関数です。
● ユーザ名・パスワード認証
ログイン画面でユーザが入力するユーザ名とパスワードを利用して、Xprioriに対してユーザが登録済みかどうかチェックします。Xprioriに問い合わせるXPathは
/ND/user_info/user[name = {ユーザ名} and password = {パスワード}] |
です。ユーザが入力したユーザ名とパスワードを持つユーザを問い合わせ、ユーザが登録されていなければ空のUserBeanを返します。
public UserBean getUser(String username,String password){ //ユーザ名とパスワードに一致するuser要素があるか調べる String userXML = xcon.query("/ND/user_info/user[name = \"" + username + "\" and password = \"" + password + "\"]"); // 検索結果XMLからDOM Documentを作成する Document doc = AddressbookUtils.loadXMLFromString(userXML); // DOM Documentから先頭のuserノードを取り出しUserBeanを作成する UserBean user = new UserBean(doc.getElementsByTagName("user").item(0)); return user; } |
ログイン画面では入力フォームによってユーザ名とパスワードを入力し、登録済みユーザであればセッションにユーザ名とユーザIDを設定し、次の画面に遷移します。冒頭でも述べましたが、住所録アプリケーションではセッションにユーザ名とユーザIDが登録されている状態をログイン状態としています。
<%@ page language="java" contentType="text/html; charset=Shift_JIS" pageEncoding="Shift_JIS"%> <% request.setCharacterEncoding("Shift_JIS"); %> <%@page import="addressbook.UserBean"%> <jsp:useBean id="searchuser" class="addressbook.SearchUserInfoBean"></jsp:useBean> <jsp:useBean id="param" class="addressbook.ParamBean"></jsp:useBean> <jsp:setProperty name="param" property="name" param="name" /> <jsp:setProperty name="param" property="password" param="password" /> <% String err_msg = ""; //エラーメッセージ // (1)パラメータnameが空かどうかチェックする if(!param.getName().equals("")){ // (2)ユーザがDBに登録済みかチェック UserBean user = searchuser.getUser(param.getName(),param.getPassword()); if(!user.getID().equals("")){ //(3)タイムアウトを30分(1800秒)に設定する session.setMaxInactiveInterval(1800); //(4)セッションにユーザIDとユーザ名を設定する session.setAttribute("username", user.getName()); session.setAttribute("userid", user.getID()); String username = (String) session.getAttribute("username"); //(5)ユーザIDが管理者(ユーザ名admin ID 000001)の場合と // 一般ユーザの場合で遷移先を変更する if(username.equals("admin") && user.getID().equals("000001")) {//管理者の場合はユーザ一覧画面に遷移 response.sendRedirect("userlist.jsp"); } else{//その他ユーザの場合はアイテム一覧画面に遷移 response.sendRedirect("itemlist.jsp"); } } else{//ユーザが登録されていない場合はエラーメッセージを表示 err_msg = "<font color=\"red\">ユーザ名とパスワードが間違っています </font>"; } } %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS"> <title>ログイン</title> </head> <body> <center> <table width="400"> <tr> <td ><h3>ログイン</h3></td> </tr> </table> <% if(!err_msg.equals("")){ %><%= err_msg %><%} %> <form action="login.jsp" method="post" name="login" id="login"> <table width="400" border="0" align="center"> <tr> <td align="right" valign="top" width="300"><b>ユーザ名</b></td> <td width="300"><input name="name" type="text" size="26"></td> </tr> <tr> <td align="right" valign="top" width="300"><b>パスワード</b></td> <td width="300"><input name="password" type="password" value="" ></td> </tr> <tr> <td width="300"> </td> <td align="left" width="300"><input name="send" value="ログイン" type="submit" ></td></tr> </table> </form> </center> </body> </html> |
処理の流れを説明します。
(1)パラメータnameが空かどうかをチェックし、初めてログイン画面に入ったかどうかを調べます。パラメータnameが空であれば、ログイン用の入力フォームを表示し、値が格納されていればログインチェックを行います。
(2)パラメータnameとパラメータpasswordを利用してログインチェックをします。SearchUserInfoBeanクラスのgetUser関数によってXprioriにユーザが登録済みかどうかの問い合わせを行います。未登録の場合は空のUserBeanが返ってくるので、ユーザIDが空でなければ登録済みと判断します。
(3)タイムアウト期間を30分に設定します。ここで期間を設定することで、ログイン後に放置されていても30分たつとログアウトとなります。
(4)ユーザ名はusernameとして、ユーザIDはuseridとしてセッションにそれぞれの値を設定します。これにより、ユーザはログイン状態となります
(5)ユーザ名が「admin」、ユーザIDが「000001」の場合は管理者ユーザなのでユーザ操作を行うためuserlist.jspに遷移します。それ以外のユーザは住所録に登録されたアイテムを閲覧するためitemlist.jspに遷移します。
実行結果は以下のようになります。
▲ログイン画面
ログアウト画面では、セッションを無効にすることでログアウトを実現します。セッションの無効化はセッションオブジェクトのinvalidate関数を実行することで可能です。invalidate関数を実行したときにユーザはログアウト状態となります。ログイン画面へ遷移するためのアンカーをつけ、ログアウト画面の完成です。コードを以下に示します。
<%@ page language="java" contentType="text/html; charset=Shift_JIS" pageEncoding="Shift_JIS"%> <% //セッションを無効にする session.invalidate(); %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS"> <title>ログアウト</title> </head> <body> <center> <table width="400"> <tr> <td ><h3>ログアウト</h3></td> </tr> </table> ログアウトしました。<br> <a href="login.jsp">ログイン画面に戻る</a> </center> </body> </html> |
ログインページで設定したセッションを見て、ログイン状態かログアウト状態かを判断するためのjspは以下のとおりです。通常ログイン、管理者ログインともに認証用クラスであるCertificationBeanを利用します。
<jsp:useBean id="cert" class="addressbook.CertificationBean" > <%-- ログインしているかチェックを行い、そうでなければログインページに遷移する--%> <% if(!cert.checkUserExist(session)){ response.sendRedirect("login.jsp"); } %> </jsp:useBean> |
通常ログインでは、CertificationBeanクラスのcheckUserExist関数にセッションオブジェクトを渡すことでチェックを行います。ログインしていない場合はログインページに遷移します。上記のコードを住所録のアイテム操作を行うitemlist.jsp、itemdetail.jsp、inputitem.jsp、confirmitem.jsp、runitem.jspの先頭に記述することで、各ページでの通常ログインチェックが可能となります。
<jsp:useBean id="cert" class="addressbook.CertificationBean" > <%-- 管理者ログインかチェックを行い、そうでなければログインページに遷移する--%> <% if(!cert.checkAdminUserExist(session)){ response.sendRedirect("login.jsp"); } %> </jsp:useBean> |
管理者ログインでは、CertificationBeanクラスのcheckAdminUserExist関数にセッションオブジェクトを渡すことでチェックを行います。こちらもログインしていない場合はログインページに遷移します。上記のコードをユーザ操作を行うuserlist.jsp、inputuser.jsp、runuser.jspの先頭に記述することで、各ページでの管理者ログインチェックが可能となります。
ただし、ログインチェックのコードをそのまま記述すると、修正が必要なときは全てのページのコードを修正しなければならないため無駄なコストがかかってしまいます。冗長なコード記述を減らすために、ここでは通常ログインのためのコードをcertification.jspに、管理者ログインのためのコードをadmincertification.jspに、それぞれ単体のjspに記述することにします。そしてログインチェックが必要な各ページでは、その先頭に
<%@include file="certification.jsp"%> |
を記述します。(管理者ログインの場合はadmincertification.jspを指定)
jspのincludeタグは外部のjspファイルを読み込むことができ、タグの位置に読み込んだjspのコードを記述するのと同じ効果があります。includeタグを利用することで冗長なコードを減らすことができました。
今回の実装によって住所録アプリケーションに認証機能を追加することができました。今後は住所録アプリケーションを利用する場合、スタートページとして必ずログイン画面にアクセスする必要があります。もしログインせずにURLを直接入力した場合は、認証機能ではじかれlogin.jspに遷移します。
スタートページ:
http://localhost:8080/address_book/login.jsp
次回はXML DB独特のセキュリティで気をつけるべきことを紹介します。
今回作成したjavaクラス・jspファイルはこちらからダウンロードすることができます。
・ jspファイルはWebアプリケーションフォルダのaddress_book/WebContentに配置してください。
・ javaファイルはWebアプリケーションフォルダのaddress_book/WebContent/WEB-INF/srcに配置してください。
また、ご自分でクラスを新規作成する場合はEclipseのメニューバーより、[ファイル]→[新規]→[クラス]を選択し、[次へ]を押してください。
次の画面で[ソースフォルダ]に「address_book/src」、[パッケージ]に「addressbook」を入力し、[名前]に適切なクラス名を入力し、[終了]を押すことで、新規のクラスを作成することができます。
今回のサンプルから、ログイン画面でログインする前に管理者ユーザを登録する必要があります。下記のXML(adminuser.xml)に管理者パスワードを設定し、XMS ConsoleのStore画面から格納してください。
<user_info> <user id="000001" > <name>admin</name> <password>{管理者パスワード(必須)}</password> </user> </user_info> |
その後、ログイン画面から管理者ユーザとしてログインし、通常作業用のユーザを登録してください。今回までに実装した機能は通常ユーザとしてログインすることで利用できます。なお、管理者ユーザはユーザ管理画面では削除できません、通常ユーザとしてtestuserを登録した場合のユーザ一覧画面は以下のようになります。
▲ユーザ一覧画面
▲このページのTOPへ