セッションを使ったログイン認証処理【Java】【JSP】【Servlet】【Tomcat】


Tomcat / Jason Brittain

セッションを使った簡単なログイン認証処理を作ってみます。

JSPとServletの遷移

JSPとServletの基本的なプログラムや遷移方法などは、以下で解説しています。

Java+Eclipse+TomcatでJSP+Servletのページ遷移
http://www.materialize.jp/art/software-services/7512/

ログイン認証処理フロー

ログイン認証処理フローを以下のように考えました。

01

1.トップページ(index.jsp)の「ログイン」から、「LoginServlet」にアクセス(GET)
2.ログインフォームページ(login.jsp)にフォワード
3.ログインフォームに入力したら、「LoginServlet」にアクセス(POST)
4.ログイン認証したら、トップページ(index.jsp)にリダイレクト

実装は以下のようになります。

[html firstline=”1″ highlight=”17″ title=”index.jsp(トップページ)”]
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>TestLogin</title>
</head>
<body>

<header>
<h1>TestLogin</h1>
<p>トップページ</p>
</header>

<main>
<div>
<p><a href="./login">ログイン</a></p>
</div>
</main>

<footer>
<small>Copyright 2014 materialize LLC.<br>http://www.materialize.jp</small>
</footer>

</body>
</html>
[/html]

リンクをクリックしてアクセスした場合、それは「GET」でアクセスしたことになります。

[html firstline=”1″ highlight=”17,18,19,20,21″ title=”login.jsp(ログインフォームページ)”]
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>TestLogin</title>
</head>
<body>

<header>
<h1>TestLogin</h1>
<p>ログインフォーム</p>
</header>

<main>
<div>
<form method="post" action="./login">
<p><input type="email" name="email" placeholder="E-MAIL" required autofocus></p>
<p><input type="password" name="password" placeholder="PASSWORD" required></p>
<p><input type="submit" value="ログイン"></p>
</form>
</div>
</main>

<footer>
<small>Copyright 2014 materialize LLC.<br>http://www.materialize.jp</small>
</footer>

</body>
</html>
[/html]

「メールアドレス」と「パスワード」でログイン認証することにします。

[java firstline=”1″ highlight=”23,27,28,42,43,44,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73″ title=”LoginServlet.java(ログイン認証)”]
/**
* Copyright 2014 materialize LLC.
* http://www.materialize.jp
*/

package jp.materialize.test.testlogin.servlet;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/**
* Servlet implementation class LoginServlet
*/
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
private static final long serialVersionUID = 1L;

private static final String USER_EMAIL = "test@materialize.jp";
private static final String USER_PASSWORD = "login";

/**
* @see HttpServlet#HttpServlet()
*/
public LoginServlet() {
super();
// TODO Auto-generated constructor stub
}

/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// ログインフォームへ遷移(フォワード).
RequestDispatcher dispatcher = this.getServletContext().getRequestDispatcher( "/login.jsp" );
dispatcher.forward( request, response );
}

/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// エンコード設定.
request.setCharacterEncoding( "UTF-8" );

// E-MAIL を取得.
String userEmail = request.getParameter( "email" );
if ( null == userEmail ) {
userEmail = "";
}

// PASSWORD を取得.
String userPassword = request.getParameter( "password" );
if ( null == userPassword ) {
userPassword = "";
}

// ログイン認証.
if ( USER_EMAIL.equals( userEmail ) && USER_PASSWORD.equals( userPassword ) ) {
// トップページへ遷移(リダイレクト).
response.sendRedirect( "./" );
} else {
// ログインフォームへ遷移(リダイレクト).
response.sendRedirect( "./login" );
}
}

}
[/java]

認証用の「メールアドレス」と「パスワード」は、それぞれ以下としました。

 メールアドレス : test@materialize.jp
 パスワード : login

本来はデータベースなどにログイン情報を保存しておき、アクセス毎にそれらを取得して認証を行いますが、今回は文字列として定義して確認しています。

「メールアドレス」と「パスワード」が一致し、ログイン認証が「OK」となった場合は、トップページへ遷移します。一致せず、ログイン認証が「NG」となった場合は、ログインフォームページを再表示します。

では、実際の画面の動きを確認してみましょう。

02

上記「ログイン」をクリックすると、「LoginServlet」から以下のページヘフォワードされます。

03

04

上記のように「デタラメ」の入力を行うと、ログイン認証が「NG」となり、ログインフォームページを再表示となります。

03

05

正しい情報を入力してアクセスすることで、ログイン認証が「OK」となり、トップページへ遷移します。

02

ログイン認証処理フローは、簡単に紹介しましたが、概ね、このような感じになります。

ただ、もちろん、これではページの遷移を追っているだけで、実際にログイン認証(ユーザーを識別)したことにはなりません。ログイン認証したら、「ログイン認証に成功した人にしか見られない」という制御が必要です。

ログイン情報をセッションに保持

Javaには、セッションを手軽に使う為に、「javax.servlet.http.HttpSession」というクラスが用意されています。

セッションは、「ユーザーのサイトアクセス単位」で管理されています。なので、セッションは「同一ユーザーのページ間のデータ共有」などに利用されていて、ログイン管理にも有効です。

ログインフォームページからログインし、その情報をセッションに保持して、

 ・ログイン中(セッションにログイン情報を保持している)
 ・ログアウト中(セッションにログイン情報を保持していない)

という判断をしてみましょう。

[java firstline=”38″ highlight=”42,43,44,45,46,47,48,49,50,51,52,53,79,80,84,85,86,87,88,89,90,91,92,93″ title=”LoginServlet.java(ログイン認証)”]
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// セッションを取得.
HttpSession session = request.getSession( true );

// ログイン情報を取得.
Map<String, String> map = (Map<String, String>)session.getAttribute( "login_user" );

// 既にログイン中.
if ( null != map ) {
// トップページへ遷移(リダイレクト).
response.sendRedirect( "./" );
return;
}

// ログインフォームへ遷移(フォワード).
RequestDispatcher dispatcher = this.getServletContext().getRequestDispatcher( "/login.jsp" );
dispatcher.forward( request, response );
}

/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// エンコード設定.
request.setCharacterEncoding( "UTF-8" );

// E-MAIL を取得.
String userEmail = request.getParameter( "email" );
if ( null == userEmail ) {
userEmail = "";
}

// PASSWORD を取得.
String userPassword = request.getParameter( "password" );
if ( null == userPassword ) {
userPassword = "";
}

// セッションを取得.
HttpSession session = request.getSession( true );

// ログイン認証.
if ( USER_EMAIL.equals( userEmail ) && USER_PASSWORD.equals( userPassword ) ) {
// ログイン情報.
Map<String, String> map = new HashMap<String, String>();
map.put( "Email", userEmail );
map.put( "Password", userPassword );

// ログイン情報をセッションに保存.
session.setAttribute( "login_user", map );

// トップページへ遷移(リダイレクト).
response.sendRedirect( "./" );
} else {
// ログインフォームへ遷移(リダイレクト).
response.sendRedirect( "./login" );
}
}

}
[/java]

ログイン情報として、ログインフォームで入力した「メールアドレス」と「パスワード」を「Map」に設定して、その「Map」をセッションに保存しています。

セッション自体の取得は、「request.getSession( true )」で行います。引数を「true」にすることで、セッションが開始していない場合に、自動でセッションを開始してくれます。引数を「false」にすると、セッションを開始していない場合には、「null」が返却されます。

 常に引数は「true」でいいんじゃないか?

と思うかもしれませんが、この引数の使い道は、例えば、セッションにはタイムアウトが存在しますから、一箇所だけ「true」でセッションを生成するように制御すれば、他の箇所で引数を「false」でアクセスした場合に「null」が返却されれば、「セッションがタイムアウトしたな」と知ることもできます。また、セッションがタイムアウトしたことに気づかずに矛盾した制御を行ってしまう危険も回避できるでしょう。

以下のように複数のServletでセッションを扱う場合に、

 AaaServlet getSession(true)  ←この一箇所でだけセッションを開始する
 BbbServlet getSession(false) ←ここで「null」が返ればタイムアウトと判断する
 CccServlet getSession(false) ←ここで「null」が返ればタイムアウトと判断する

こうすることでタイムアウトを判断することもできます。

[html firstline=”1″ highlight=”2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,34,35,36,37,38,39,40,41″ title=”index.jsp(トップページ)”]
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ page import="java.util.Map" %>
<%
Map<String, String> map = (Map<String, String>)session.getAttribute( "login_user" );

String userEmail = "";
String userPassword = "";
String hiddenLogin = "";
String hiddenLogout = "";
if ( null == map ) {
// ログアウト中.
hiddenLogout = "hidden";
} else {
// ログイン中.
userEmail = (String)map.get( "Email" );
userPassword = (String)map.get( "Password" );
hiddenLogin = "hidden";
}
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>TestLogin</title>
</head>
<body>

<header>
<h1>TestLogin</h1>
<p>トップページ</p>
</header>

<main>
<div <%= hiddenLogin %>>
<p><a href="./login">ログイン</a></p>
</div>
<div <%= hiddenLogout %>>
<p>E-MAIL : <%= userEmail %></p>
<p>PASSWORD : <%= userPassword %></p>
<p><a href="./logout">ログアウト</a></p>
</div>
</main>

<footer>
<small>Copyright 2014 materialize LLC.<br>http://www.materialize.jp</small>
</footer>

</body>
</html>
[/html]

セッションにログイン情報を保持した「Map」が存在していれば、「ログイン中」と判断し、「Map」が存在していなければ、「ログアウト中」と判断します。

「ログイン中」のみ、ログイン情報である「メールアドレス」と「パスワード」を表示しています。

06

ログアウト処理

前述のトップページ(index.jsp)内で記述してしまいましたが、「ログアウト」処理を追加します。

「ログイン情報をセッションに保持している」ことが、イコール、「ログインしている」ことになっていますから、この逆の、「セッションからログイン情報を破棄する」ことが、イコール、「ログアウトしている」ことになります。

セッションそのものを終了してしまえばいいです。

1.トップページ(index.jsp)の「ログアウト」から、「LogoutServlet」にアクセス(GET)
2.セッションを終了
3.トップページ(index.jsp)にリダイレクト

では、実装です。

[java firstline=”1″ highlight=”20,36,37,38,39,40,41,42,43,44,45″ title=”LogoutServlet.java(ログアウト処理)”]
/**
* Copyright 2014 materialize LLC.
* http://www.materialize.jp
*/

package jp.materialize.test.testlogin.servlet;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/**
* Servlet implementation class LogoutServlet
*/
@WebServlet("/logout")
public class LogoutServlet extends HttpServlet {
private static final long serialVersionUID = 1L;

/**
* @see HttpServlet#HttpServlet()
*/
public LogoutServlet() {
super();
// TODO Auto-generated constructor stub
}

/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// セッションを取得.
HttpSession session = request.getSession( false );

if ( null != session ) {
// セッションを終了.
session.invalidate();
}

// トップページへ遷移(リダイレクト).
response.sendRedirect( "./" );
}

/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
}

}
[/java]

非常に単純で、セッションが開始されていれば、それを「session.invalidate()」で終了させるだけです。

これで基本的な「ログイン」「ログアウト」の仕組みはできました。

セッションのタイムアウト

セッションのタイムアウトを制御してみましょう。

ログイン認証を行ったWEBサイトで、無操作で放置していたら、いつの間にかログアウトされていた、なんて経験がある方も多いと思います。これはセッションのタイムアウトによるものが多いでしょう。

「Tomcat」において、デフォルトのタイムアウトは無操作から「30分(1800秒)」となっています。

これを「1分(60秒)」に変更して、タイムアウトを確認してみます。

[java firstline=”60″ highlight=”84,85″ title=”LoginServlet.java(ログイン認証)”]
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// エンコード設定.
request.setCharacterEncoding( "UTF-8" );

// E-MAIL を取得.
String userEmail = request.getParameter( "email" );
if ( null == userEmail ) {
userEmail = "";
}

// PASSWORD を取得.
String userPassword = request.getParameter( "password" );
if ( null == userPassword ) {
userPassword = "";
}

// セッションを取得.
HttpSession session = request.getSession( true );

// ログイン認証.
if ( USER_EMAIL.equals( userEmail ) && USER_PASSWORD.equals( userPassword ) ) {
// セッションタイムアウトを設定(秒).
session.setMaxInactiveInterval( 60 );

// ログイン情報.
Map<String, String> map = new HashMap<String, String>();
map.put( "Email", userEmail );
map.put( "Password", userPassword );

// ログイン情報をセッションに保存.
session.setAttribute( "login_user", map );

// トップページへ遷移(リダイレクト).
response.sendRedirect( "./" );
} else {
// ログインフォームへ遷移(リダイレクト).
response.sendRedirect( "./login" );
}
}

}
[/java]

セッションのタイムアウト設定は、「session.setMaxInactiveInterval(秒)」で行います。引数は「」です。この引数に「マイナス値(-1)」を設定すると「タイムアウトしない」という設定になります。

ユーザーにとっては何度もログインするのは面倒なので、タイムアウトしない設定は嬉しいですが、銀行のようなWEBバンクでタイムアウトしないとなると、少々セキュリティ面で不安になります。

もちろんですが、ケースバイケースで使い分けるのが好ましいでしょう。

06

サーブレットコンテナがクライアントから最後にリクエストを受けてからの時間でタイムアウトは判断されるので、「1分(60秒)」はブラウザの更新ボタンもクリックしてはいけません。

02

「1分(60秒)」経過後、ブラウザの更新ボタンをクリックすると、上記の通り、ログアウトされています。

プロジェクトファイル一式

この記事を書く為に作成した、「ログイン認証処理のプロジェクトファイル一式」を、以下のサイト(note)に公開しています。

プロジェクトファイル一式とは、以下のように「エクスポート」したものです。

07

しかし、勉強の為には、自分の手で一から環境を構築し、自分の手で実装するのが最も効果があります。

 それでも面倒くさい!
 一式がすぐほしい!

という方は、以下のサイト(note)にアクセスしてください。何度も言いますが、学ぶ為には、自分の手で実装することを強くオススメします。

セッションを使ったログイン認証を作成する【Java】【JSP】【Servlet】【Tomcat】
https://note.mu/materialize/n/nfc8aade7ac94

(平成26年10月5日 アシベズヘア@ashibehair_mfacebooknoteSUZURI

unnamed

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です