セッションを使ったカスタム認証

広告

ここまではTomcatで標準で用意されている認証の方法を確認して来ましたが、標準で用意されている方式は簡易に利用できる反面、カスタマイズするにも限界があります。例えばログアウト処理なども行うことが出来ません。

そこでここからは認証を方式を自分で一から構築する方法を試して行きます。今回は次のようにしてみます。

ユーザー名とパスワードはHTMLページ上のフォームを使う
ユーザー管理はデータベースを利用する
送られてきたユーザー名とパスワードでデータベースを検索して認証する
認証が通った場合はセッションに記録する
保護されているページではセッションの有無をチェックする
ログアウト機能を付ける

少し長くなるので細かく分けて作成してみます。

認証の対象となるサーブレットの作成

まず保護する対象となるサーブレットを作成します。これらのサーブレットにアクセスがあった場合にはユーザー認証が行われているかどうかをチェックし、認証済みならば内容を表示し、認証されていなければログイン画面へ移動します。

認証が行われているかどうかはセッションを使います。セッションの「login」データがあるかどうかをチェックします。

HttpSession session = request.getSession(false);

if (session == null){
  /* まだ認証されていない */
  session = request.getSession(true);

  response.sendRedirect("/auth/Login");
}else{
  Object loginCheck = session.getAttribute("login");
  if (loginCheck == null){
    /* まだ認証されていない */
    response.sendRedirect("/auth/Login");
  }
}

セッションの有無を確認し、セッションが開始されていなかったりセッション開始されていても「login」に関するデータが無い場合はログイン画面へ移動します。セッションが開始されていない場合もこの画面でセッションを開始します。

またログイン画面に一度飛ばした後で、本来アクセスしたかったURLへ移動してあげる必要がありますので、どのサーブレットに対してアクセスしようとしていたのかを保存しておきます。

リクエストがあったURLを取得するには「HttpServletRequest」インターフェースで定義されている「getRequestURI」メソッドを使います。

Returns the part of this request's URL from the protocol name up to the 
query string in the first line of the HTTP request. The web container 
does not decode this String. For example:

  First line of HTTP request          Returned Value
  -----------------------------------------------------
  POST /some/path.html HTTP/1.1       /some/path.html 
  GET http://foo.bar/a.html HTTP/1.0  /a.html 
  HEAD /xyz?a=b HTTP/1.1              /xyz 

To reconstruct an URL with a scheme and host, use 
HttpUtils.getRequestURL(javax.servlet.http.HttpServletRequest). 

Returns:
  a String containing the part of the URL from the protocol name up
    to the query string

本来アクセスしようとしたURLを取得したらセッションの「target」に保存しておきます。

String target = request.getRequestURL();
session.setAttribute("target", target);

結果的に次のようなサーブレットになります。

CustomAuth1.java

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class CustomAuth1 extends HttpServlet {
  public void doGet(HttpServletRequest request, HttpServletResponse response)
    throws IOException, ServletException{

    response.setContentType("text/html; charset=Shift_JIS");
    PrintWriter out = response.getWriter();

    String target = request.getRequestURI();

    HttpSession session = request.getSession(false);

    if (session == null){
      /* まだ認証されていない */
      session = request.getSession(true);
      session.setAttribute("target", target);

      response.sendRedirect("/auth/Login");
    }else{
      Object loginCheck = session.getAttribute("login");
      if (loginCheck == null){
        /* まだ認証されていない */
        session.setAttribute("target", target);
        response.sendRedirect("/auth/Login");
      }
    }

    out.println("<html>");
    out.println("<head>");
    out.println("<title>ユーザー認証テスト</title>");
    out.println("</head>");
    out.println("<body>");

    out.println("<p>テストページ1</p>");

    out.println("<p><a href=\"/auth/CustomAuth2\">テストページ2へ</a></p>");

    out.println("</body>");
    out.println("</html>");
  }
}

またほぼ同じ内容のサーブレットをもう1つ作っておきます。この2つのサーブレットはお互いにリンクが張ってあります。

CustomAuth2.java

実際に試してみる

ではこの時点でのテストを行っておきます。まだユーザー認証用の「Login」サーブレットがありませんので、暫定的に次のサーブレットを用意します。

本来であればユーザー名とパスワードを入力してもらいユーザー認証を行いますが、取りあえず認証を全て通して本来アクセスするはずのURLへのリンクを設定するだけにしてあります。

Login1.java

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class Login1 extends HttpServlet {
  public void doGet(HttpServletRequest request, HttpServletResponse response)
    throws IOException, ServletException{

    response.setContentType("text/html; charset=Shift_JIS");
    PrintWriter out = response.getWriter();

    out.println("<html>");
    out.println("<head>");
    out.println("<title>ログインページ</title>");
    out.println("</head>");
    out.println("<body>");

    out.println("<h1>ログイン画面</h1>");

    out.println("<p>本来はここでログインするためのフォームが表示されます</p>");

    HttpSession session = request.getSession(false);

    if (session == null){
      /* セッションが開始されずにここへ来た場合は無条件でエラー表示 */
      out.println("<p>不正なアクセスです</p>");
    }else{
      /* 今回は無条件で認証を許可する */
      out.println("<p>認証が行われました</p>");

      /* 認証済みにセット */
      session.setAttribute("login", "OK");

      /* 本来のアクセス先へのリンクを設定 */
      String target = (String)session.getAttribute("target");
      out.println("<a href=\"" + target + "\">ページを表示する</a>");
    }

    out.println("</body>");
    out.println("</html>");
  }
}

web.xmlファイルは次の通りです。

<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
   http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
  version="2.4">

  <servlet>
    <servlet-name>CustomAuth1</servlet-name>
    <servlet-class>CustomAuth1</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>CustomAuth1</servlet-name>
    <url-pattern>/CustomAuth1</url-pattern>
  </servlet-mapping>

  <servlet>
    <servlet-name>CustomAuth2</servlet-name>
    <servlet-class>CustomAuth2</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>CustomAuth2</servlet-name>
    <url-pattern>/CustomAuth2</url-pattern>
  </servlet-mapping>

  <servlet>
    <servlet-name>Login</servlet-name>
    <servlet-class>Login1</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>Login</servlet-name>
    <url-pattern>/Login</url-pattern>
  </servlet-mapping>
</web-app>

それでは一度サーブレットを再起動してから、ブラウザで「http://localhost:8080/auth/CustomAuth1」へアクセスして下さい。

カスタム認証

「CustomAuth1」サーブレットへアクセスしてもセッションに必要な情報が保存されていませんので「Login」サーブレットへリダイレクトされます。今回はテスト用なのでこの時点で認証が行われていますので、「ページを表示する 」をクリックすれば本来アクセスするはずの「CustomAuth1」サーブレットへアクセスします。

カスタム認証

今度はセッションが既に開始されていて、必要な情報がセッションに登録されているので「CustomAuth1」サーブレットの中身が表示されます。

また「CustomAuth1」サーブレットからリンクが張ってある「CustomAuth2」サーブレットにも認証のチェックが入っていますが、既に認証済みですのでリンクをクリックすれば普通に表示されます。

カスタム認証

それでは次のページでログイン画面の部分を作成します。

( Written by Tatsuo Ikura )

Profile
profile_img

著者 / TATSUO IKURA

プログラミングや開発環境構築の解説サイトを運営しています。