前回は、フォームからデータをサーブレットに渡すところまでをやりました。

今回は、今までの知識を応用してログイン処理を作ってみます。

前回学んだリクエスト属性を使えば、入力画面に入力した内容をもとにJSPの出力画面に情報を表示できました。しかし、実はリクエスト属性は複数ページを跨いで情報の保持ができません。ステートレスといって1対のリクエストとレスポンスで終了してしまうのです。

そこで必要となるのがセッション属性です。セッション属性を使うことで複数ページを跨いで情報の保持ができます。そしてログイン処理は複数ページを跨いで情報を保持する必要のある典型的な処理です。この章ではそんなセッション属性の特徴をログイン処理を実装することにより体験的に学びます。

1. ログイン処理とは

まずは一般的なログイン処理についてまとめておきましょう。皆さんも是非、普段お使いのWebアプリケーションで実験してみてください。(あるいは講師の実演をご覧ください)

突然ですがログインに関する質問です。

  • ①一般にWebシステムにログインするにはどのような情報が必要ですか?
あなたの答え:
  • ②ログイン前と後では何が違いますか?
あなたの答え:
  • ③あるブラウザでログイン中に同じブラウザで他のタブを開いたら新しい方のタブでログインは継続されますか?
あなたの答え:
  • ④あるブラウザ(例えばクローム)でログイン中に新しいブラウザ(例えばエッジ)を起動したら新しいブラウザでログインは継続されますか?
あなたの答え:
  • ⑤どんなときにWebアプリからログアウトしますか?
あなたの答え:

上記の実験からも明らかなように一般的にはログインとは以下のような処理を指します。


①ユーザーはブラウザからIDとパスワードといった自分だけが知る情報を使ってログインする

②ログイン後はそのユーザーだけが見られるページに遷移できるようになる

③ログイン中は同じブラウザで他のタブを開いてもログインされたままである

④しかし、同じユーザーが別のブラウザで同じページにアクセスしても再度ログイン処理が必要である

⑤ログアウト処理をする、ブラウザを終了するまたは一定時間(大抵は30分間だが銀行のように5分程度と短い場合も)が経過するとログアウトする


上記結果④から考えるとログイン処理はブラウザごとに固有の情報を利用していると推測できます。

また、結果⑤から考えるとログアウトのタイミングは技術者が個々に設定していることが分かります。

本章では、ログイン処理を実現するにはセッションという仕組みを理解することが必要になるということ、さらに同一セッションで有効なセッション属性を使うことでリクエスト属性では実現できなかった複数リクエストを跨いだデータ保持が可能になるということを解説していきたいと思います。例えば、オンラインショッピングで買い物かごの仕組みが実現できるのは、かごの中身をこのセッション属性に入れているから複数ページに渡ってデータを保持できるのです。次の商品を見に行くたびに買い物かごの中身がクリアされてしまっては使い物になりませんからね。

セッション属性とは開始してから破棄されるまで同一ブラウザで有効なスコープです。

セッションの破棄は、「ログアウト処理」、「ブラウザを終了する」または「セッションタイムアウト」により実行されます

実は、前章までの知識で実現できるのは上記「①ユーザーはブラウザからIDとパスワードといった自分だけが知る情報を使ってログインする」だけです。

1.1. IDとパスワードでログインする

以下の図が処理の概要です。

ユーザーがブラウザからユーザーIDとパスワードを入力してログインボタンを押下します。

サーブレットはIdとpasswordが正しいことをチェックします。

①IDとパスワードが正しければ、messageをmember-only.jspにフォワードします。

②IDとパスワードが正しくなければ、エラー画面を表示します。

セッション属性
ログイン処理の概要

2. ログイン処理の実装

下図のようなログイン画面を作ります。

セッション属性
ログイン画面

ログイン画面

以下のlogin.jspはユーザーIDとパスワードの入力を求める画面です。

ソースコードを読み込んで質問に答えてください。

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>login.jsp</title>
        <style>
            #center {
                display:flex;
                justify-content: center;
                align-items: center;
                text-align: center;
                height: 100vh;
            }
        </style>
    </head>
    <body>
        <div id="center">
            <form action="${pageContext.request.contextPath}/Login1" method="post">
                ユーザーID:<input type="text" name="id" required><br>
                パスワード:<input type="password" name="pass" required><br>
                <button type="submit">ログイン</button>
            </form>
        </div>
    </body>
</html>
  • ①<style>タグで囲まれた部分を英字3文字で何といいましたか?
あなたの答え:
  • ②「method="post"」このメソッドの特徴は何でしたか?
あなたの答え:

サーブレットのソースコード

以下のLogin1Servlet.javaはIDとパスワードを認証するサーブレットです。

プログラムを読み込んで質問に答えてください。

package controller;

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;

@WebServlet(urlPatterns = { "/Login1" })
public class Login1Servlet extends HttpServlet {

	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		String id = request.getParameter("id");
		String pass = request.getParameter("pass");

		if ("imai".equals(id) && "p".equals(pass)) {
			request.setAttribute("id", id);
			request.getRequestDispatcher("view/member-only.jsp").forward(request, response);
		} else {
			response.sendRedirect("view/login-error.jsp");
		}
	}
}
  • ①上記のif文でtrueの場合はどうなりますか?
あなたの答え:
  • ②「request.setAttribute…」ではどんな属性に対してどんな名前でどんなデータを格納していますか?
あなたの答え:

setAttribute()メソッドの引数は以下のようになっています。

setAttribute(String name, Object value)

第2引数はセッションに格納したいインスタンスです。Object型なのでJavaのインスタンスであれば何でも格納できます。

第1引数は名前です。変数名のようなもので JSPのEL式でこの名前で取り出すことができます。

次に、if文でfalseの場合、すなわちIDまたはパスワードが間違った場合を見てみましょう。

ここでは、フォワードを使っていませんね。フォワードを使ってもよいのですが、フォワードですとアドレスバーの表記が変わらないため “ログインできていない” ことが伝わりづらいため、リダイレクト処理というものを行っています。

3. フォワードとリダイレクト

画面遷移の方法にはフォワードとリダイレクトがあります

英語の【redirect】には「re=再び」「direct=方向づける」という意味があります。データの出力先などを変更するという意味でIT業界では広く使われる言葉です。

その特徴をまとめると以下の図の通りです。

セッション属性
forwardとredirectの違い

特に注目いただきたいのは以下の4点です。

「2.ソースコード」のところでフォワードがrequestのメソッドであるのに対して、リダイレクトはresponseのメソッドであるという点

「3.遷移先のパス指定方式」のところで相対パス指定ができる点(混乱を避けるためこの後のパス指定は相対パスに統一します)

「4.典型的な処理の流れ」で、フォワードがリクエストとレスポンスを1往復交わすのに対して、リダイレクトは2往復であるという点

「5.リクエストスコープ」でリダイレクトはリクエスト属性を引き継がない

ちなみに、リダイレクトは「5.転送可能範囲」で外部サーバーOKとなっています。したがって上記の記述を以下のようにルートパスでも、絶対パスでも、同じ結果が得られるのです。フォワードと違ってパス指定のルールがそのまま適用できると考えれば理解しやすいでしょう。(上図の「3. 遷移先のパス指定方式」参照)

この研修中はリダイレクトには相対パスで指定することにします。なぜならそれが一番短く記述できるうえにフォワードと共通だからです。

response.sendRedirect("view/login-error.jsp");

なお、リダイレクト先は外部サーバーも可能です。

response.sendRedirect("https://saycon.co.jp/");

ひとまず、「リクエスト属性とフォワード」、「セッション属性とリダイレクト」はセットであるそう覚えていただいても研修期間中は問題ありません。

フォワードとリダイレクトをコールセンターの受付に例える

コールセンターの受付に例えると、

  • フォワードは電話を該当部署に転送する
  • リダイレクトはメーカーに電話をかけ直してもらう

例題

以下の画面遷移はフォワードとリダイレクト、どちらを使うべきでしょうか?

  1. フォームから入力した内容をサーブレットでjspにフォワードして1度だけ画面表示させる場合
  2. 外部のサイトのページを表示する場合(例:https://www.yahoo.co.jp/)
  3. ユーザーがログインに成功した後、ユーザー専用画面(Mypage)を表示して、その中で画面遷移した場合にすべてのページで「こんにちはxxさん」と表示したい場合

ログインエラー画面のソース

以下のlogin-error.jspはログインエラー時の画面、menber-only.jspはログイン成功時の画面です。

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>login-error.jsp</title>
        <style>
            #content1{
                color: red;
            }
        </style>
    </head>
    <body>
    <center>
        <div id="content1">
            <h2>ログインエラーが発生しました。</h2>
        </div>
    </center>
    <p style="text-align:center"><a href="login.jsp">ログイン画面へ戻る</a></p>
    </body>
</html>
<%@ page pageEncoding="UTF-8" %>
メンバーだけのページです。
ようこそ${requestScope.id}さん

member-only.jspの3行目の以下の記述は、リクエスト属性を指定する書き方です。後で学ぶセッション属性を指定する書き方と比べてみてください。

${requestScope.id}

4. getメソッドとpostメソッドの違い

HTMLでも学んだ下図にあるpostメソッドの特徴をログイン処理に応用しましょう。(ちなみにシステム開発演習中は無理にpostメソッドを使う必要はありません。とりあえず全てgetメソッドにしても大丈夫です。なぜなら、postメソッドには①サーブレットが単体で実行できなくなる。②リクエストパラメータが見えなくなる。という特徴があるためです。)

getPost
getメソッドとpostメソッドの違い

実験

もしも、postリクエストを使わずにgetリクエストを使ったらログイン処理としてどのような不都合があるでしょうか?

上記login.jspとLogin1Servlet.javaを書き換えて試してみましょう。

確かめた結果:

例題

以下のactionはgetとpostのどちらを使うべきでしょうか?

  1. 商品の検索画面でキーワードを送信し、一時的な結果をJSPに表示する場合
  2. ユーザーパスワードなどの機密情報を送信するなどURLにパラメータを表示させたくない場合
  3. ウェブフォームを介して大量のデータ(例:ファイルアップロード)を送信する場合

では、ここでもう少し踏み込んでgetメソッドとpostメソッドの違いについて見ていきましょう。

ブラウザには開発用のツール(ディベロッパーツール)があります。例えば、当社の新人エンジニア研修で使用しているChromeの場合はブラウザの画面を右クリックして「検証」を選びます。(ショートカットキーはF12)

当初は英語で表示されると思いますが、以下の設定画面で日本語にすることもできますので講師から説明を受けてください。

セッション属性
Google Chromeのディベロッパーツールを日本語化する

下図にある「ネットワーク」のタブをクリックします。

セッション属性
ネットワークタブ

F5キーを押してリクエストとレスポンスのやり取りをもう一度実行します。

次に、「名前」のところでサーブレットのURLパターン(例.Login1)を選択して、右の「ヘッダー」をクリックします。

下図の上がpostメソッド、下がgetメソッドの表示例です。

セッション属性
セッション属性
postメソッドとgetメソッドの違い
  • ①postとgetでリクエストURLはどう違いますか?
あなたの答え:

ただし、postリクエストにするとリクエストパラメータがアドレスバーに表示されないというだけです。上図の「Payload」というタブを開くとパラメータを見ることができてしまいます。httpsを使った暗号化などをしないとパケットを中継する機器からは丸見えです。くれぐれも誤解のないように。(https通信ですと「Payload」のタブ自体が無くなります)

ちなみにステータスコードが「200 OK」になっていますね。HTTPプロトコルにおいてステータスコードとは、Webサーバからのレスポンスの意味を表す3桁の数字です。

ここで当社の新人エンジニア研修で見かけるステータスコードを表5.1にまとめておきます。

ステータスコード意味
200正しく表示されている
403アクセスを禁止されている
404ページが見つからない
405このURLではサポートされていないメソッドでアクセスした
500サーバ内部エラー
表5.1 ステータスコード

調べてみましょう

余裕があれば、ジョークHTTPステータスコードの418を調べてみましょう。

次に下図のように「Cookie」をクリックしてください。

名前に「JSESSIONID」、値に32桁の16進数が入っていますね。

セッション属性
JSESSIONID

Cookie とは、Webサイトの訪問者の情報を一時的にクライアントに保存するための仕組みです。Cookie の目的はユーザーの識別です。近年 Cookie を使った広告のトラッキングが問題になっていることをご存じの方もいるかと思います。

そして今回作りたいログイン処理もこの Cookie を使った仕組みなのです。


次に、 JSESSIONID とはJ2EEを使用したWebアプリケーションにおいてデフォルトで使用されるセッションIDを表す番号です。JSESSIONID の先頭のJはJavaのJなのですね。

セッション【session】とは、ブラウザとWebサーバの一連の複数のリクエストとレスポンスの活動期間のことです。(例えば、複数の質問に回答する時間をQ&Aセッションといいますね。英語のsessionには複数の送信と受信の一連の流れ、というような意味があるのです)このセッションごとにIDが付いているわけです。

実は一般的なログイン処理はこのセッションの仕組みを利用しているのです。(注:他にも方法はあります)

次はこのセッションの仕組みを使ってログイン処理を実装しましょう。

5. セッションIDの仕組み

下図を使いセッションIDの仕組みを解説します。

  1. クライアント(ブラウザ)からサーバにリクエストが送られます
  2. サーバはセッションIDを生成します
  3. サーバはレスポンスにセッションIDを入れてクライアントに返します
  4. クライアントはセッションIDをCookieに保存します
  5. 以降、クライアントは全てのリクエストにセッションIDを入れて送ります
  6. サーバはクライアントから送られたセッションIDとサーバに保存されたセッションIDを照合することによりユーザーを認証します
  7. それ以降は上記5,6の手順でリクエストとレスポンスを繰り返します
  8. セッションIDのタイムアウト(デフォルトでは30分間)または破棄(大抵はログアウト)するまでセッションは有効です

パスワードとは異なりセッションIDは1回限りの意味のない16進数なので盗まれたとしても実害は少ないです。

(ただし、そのセッションIDを悪用する攻撃もありますので注意が必要です。もちろん現在皆さんがお使いのブラウザでは簡単には盗まれないようになっていますのでひとまずは安心して大丈夫ですが)

セッション属性
セッションIDの仕組み

セッションIDを暗証番号キーに例える

セッションIDをホテルの暗証番号キーに例えると、

  • 暗証番号キーを知らないと部屋には入れない
  • 暗証番号キーを知っていれば部屋に入って荷物を出し入れできる

調べてみましょう

Cookieの保存場所を見てみましょう。

当社の新人研修で使用しているGoogle Chromeの場合のCookieは、C:\Users\Your_User_Name\AppData\Local\Google\Chrome\User Data\Default\Network
にある単一のファイルに保存されています(Your_User_Nameをあなたの実際のユーザーアカウント名に置き換えてください)。

なお、このファイルはユーザーが読むことや編集することはできませんので試してみましょう。

調べたことのメモ:

5.1. セッションを使ったログイン処理を実装する

セッションを使って次のログイン処理(再掲)を実現することにしましょう。以下の②~⑥に対応した処理を作っていきます。

ユーザーはブラウザからIDとパスワードといった自分だけが知る情報を使ってログインする(対応済み)

②ログイン後はそのユーザーだけが見られるページに遷移できるようになる

③ログイン中は同じブラウザで他のタブを開いてもログインされたままである

④しかし、同じユーザーが別のブラウザで同じページにアクセスしても再度ログイン処理が必要である

⑤ログアウト処理をする、ブラウザを終了するまたは一定時間(大抵は30分間だが銀行のように5分程度と短い場合も)が経過するとログアウトする

⑥ログアウトしてからブラウザの戻るボタンを押すと元のログイン状態に戻らないセキュリティを強化したサイトが存在する


セッションIDに紐づく形でセッション属性という記憶領域が用意されています。

セッション属性の概要は下図のとおりです。

リクエスト属性に似ていますが、リクエスト属性は1つのリクエストだけがスコープでした。しかし、セッション属性は複数のリクエストにまたがって有効なスコープです。セッション属性は同一ブラウザであれば全てのページで有効なスコープであるともいえます。したがってセッションを使ったWebアプリケーションをテストする際には注意が必要です。例えばWebブラウザのタブを全て閉じてからテストしないと、セッション属性にデータが残っていて意図しない動作をすることがあります。

セッション属性
セッション属性に入れたデータは複数リクエストをまたいで利用できる

<login.jsp>はコピーして <login2.jsp> として使用します。

飛び先だけ次のurlPatterns<Login2>に変更しておいてください。

package controller;

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;

@WebServlet(urlPatterns = { "/Login2" })
public class Login2Servlet extends HttpServlet {

	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		String id = request.getParameter("id");
		String pass = request.getParameter("pass");

		if ("imai".equals(id) && "p".equals(pass)) {

			HttpSession session = request.getSession(true);

			session.setAttribute("id", id);
			response.sendRedirect("view/member-only2.jsp");
		} else {
			response.sendRedirect("view/login-error.jsp");
		}
	}
}
  • ①<Login1Servlet.java> からの変更点はなんですか?
あなたの答え:

request.getSession(true);

このように記述することでセッションが存在する場合にはそのセッションを返し、存在しない場合には新しいセッションを作成します。この記述は初めてアクセスするサーブレットでセッションを作成する場合に使用すると考えてください。最初のアクセスやログインページなど、セッションがまだ存在しない可能性が高い場面です。

リクエスト属性はdoPost()やdoGet()メソッドの引数だったため宣言が不要でしたね。対して、セッション属性はHttpSessionクラスの宣言が必要なのですね。

session.setAttribute("id", id);

このように記述することでセッション属性にオブジェクトを名前をつけて保存することができます。メソッド名(setAttribute)は全くリクエスト属性のときと同じと覚えましょう。

  • ①セッション属性から属性を取得するメソッドの書き方を予測してみてください。
あなたの答え:

ログインしないと見られないページは次のmember-only2.jspに記述されています。

menber-only2.jspの以下の記述

${sessionScope.id}

は、セッション属性を指定する書き方です。以前のリクエスト属性を指定する書き方と比べてみてください。

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>member-only2.jsp</title>
    </head>
    <body>
        <br>メンバーだけのページです。
        ようこそ${sessionScope.id}さん<br>

        <a href="video.jsp"  target="_blank">別のタブでビデオを見る</a><br>
    </body>
</html>
  • ①一旦ブラウザを閉じたあと、この <member-only2.jsp>を単体で実行したらどうなりますか?
あなたの答え:

以下のvideo.jspはログイン後にメンバー専用でビデオが見られるページです。

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>video.jsp</title>
    </head>
    <body>
        こんにちは${sessionScope.message}さんビデオを楽しんでください。<br>

        <iframe width="560" height="315" src="https://www.youtube.com/embed/LA95vAwTn-M" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

    </body>
</html>

5.2 セッション属性とリクエスト属性の使い分け

セッション属性は有効範囲が広いため、新人エンジニアの中にはリクエスト属性ではなくセッション属性に何でも入れようとする人がいます。しかし、それは良くないことです。なぜなら、アプリケーションサーバのメモリを圧迫してしまうからです。

リクエスト属性は一つのリクエスト間だけ有効

セッション属性は複数のリクエスト間でも有効

ということは、「リクエスト属性はすぐにサーバーのメモリから消えるが、セッション属性は残ってしまう」のです。この性質を理解して適切に使い分けましょう。「使い捨て」の方が都合の良いことも多いものです。特にITの世界では「使い捨て」といってもデータですから環境問題は生じないので積極的に使い捨てましょう。

下図はセッション属性とリクエスト属性の比較です。

セッション属性
セッション属性とリクエスト属性の比較

例題

以下の情報はリクエスト属性、セッション属性どちらに保存すべきでしょうか?

  1. ユーザーがログインしている間は常に必要となる登録ユーザーの個人情報
  2. ユーザーが買い物かごに入れた商品情報
  3. データベースから取得した画面に表示するのための商品情報

6. メニューをインクルードする

ここで、下図のようにWebアプリケーションらしくログイン後の各ページにはメニューがある状態にしたいと思います。

セッション属性
メインメニューのあるアプリケーション

メニューとして以下のmenu.jspをご用意ください。

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<a href="${pageContext.request.contextPath}/login3.jsp">ログイン</a>
<a href="${pageContext.request.contextPath}/Logout">ログアウト</a>
<a href="#">商品検索</a>
<a href="#">買い物かご</a>

リンクにpageContext.request.contextPath を使用している理由は、各JSPページがどのコンテキストパスで提供されているかに関係なく、リンクが正しく動作するようにするためです。メニューが異なるJSPページに組み込まれる場合、それぞれのJSPページが異なるコンテキストパス(ディレクトリ構造)で提供される可能性があるからです。

また、商品検索や買い物かごのリンク先は今はダミー(#)です。


ログインのリンク先のJSPはこれから以下のlogin3.jspを作ります。

  • ①Login3Servlet.javaはLogin2Servlet.javaを参考に各自作成すること
□作成できました
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<title>login3.jsp</title>
<style>
    #center {
        height: 200px;
        width: 300px;
        position: absolute;
        left: 50%;
        top: 50%;
        margin-top: -100px;
        margin-left: -150px;
        text-align: center;
    }
</style>
</head>
<body>
<%@ include file="view/menu.jsp" %>
    <div id="center">
        <form action="${pageContext.request.contextPath}/Login3" method="post">
            ユーザーID:<input type="text" name="id" required><br>
            パスワード:<input type="password" name="pass" required><br>
            <button type="submit">ログイン</button>
        </form>
    </div>
</body>
</html>
  • ①「<%@ include file="view/menu.jsp" %>」この記述は何をしていると推測されますか?
あなたの答え:

7. ログアウト処理の実装

次にログアウト処理を考えてみましょう。

私達はこれまでセッションを発行することによって同一セッションであるかどうかを判断し、ログイン状態を保ってきました。したがって、ログアウト処理はセッションを終了することによって実現できそうです。

以下のLogout.javaはセッションを無効にした上で、ログイン画面にリダイレクト処理をするサーブレットです。このプログラムを読んで質問に答えてください。

package controller;

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;

@WebServlet(urlPatterns = { "/Logout" })
public class LogoutServlet extends HttpServlet {

	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		HttpSession session = request.getSession(false);

		if (session != null) {
			session.invalidate();
		}

		// ログインページなどにリダイレクト
		response.sendRedirect("login3.jsp");
	}
}

request.getSession(false);

このように記述することでセッションが存在する場合にはそのセッションを返し、存在しない場合にはnullを返します。引数にtrueを指定した場合と異なる挙動ですので気を付けてください。

この記述は既にセッションが存在することが前提の場合、例えばユーザーがログイン後に利用するページでは、request.getSession(false) を使用します。これにより、無駄なセッションが作成されず、セッションが存在しない場合は適切に処理できます。2回目以降にサーブレットにアクセスする場合に使います。

  • ①「session.invalidate();」という記述では何をしていると推測されますか?
あなたの答え:

セッションを無効にすることで、セッションに保存されているユーザー情報やその他の機密情報が保護され、不正なアクセスから守られます。また、不要になったセッションデータをクリーンアップすることで、サーバーのメモリを圧迫しません。

例題

ログイン後のページ全てにmenu.jspを組み込みなさい。

例題

各社でプロジェクト名を決めて0からログインの仕組みを作りなさい。可能な限りこのテキストでやったことを再現すること。(できるだけテキストを見ないように)


8. セッションのまとめ

最後にセッションの扱い方をサンプルコードにして、コメントを付けておきましたので確認してください。

package controller;

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;

@WebServlet(urlPatterns = { "/SessionExample" })
public class SessionExampleServlet extends HttpServlet {
	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		
		// セッションを取得、セッションが存在しない場合は新規作成
		HttpSession session = request.getSession(true);
		
		// セッションIDを取得
		System.out.println(session.getId());

		// セッション属性の設定
		session.setAttribute("username", "exampleUser");

		// セッション属性の取得、セッション属性はObject型で設定されるためキャストが必要
		String username = (String)session.getAttribute("username");
		

		// 取得した文字列を表示
		System.out.println(username);
		

		// セッション属性の削除
		session.removeAttribute("username");

		// セッション属性の取得、ただし、削除されているために出力はnull
		System.out.println(session.getAttribute("username"));

		// しかし、セッション自体は存在しているためIDは取得できる
		System.out.println(session.getId());

		// セッションの無効化
		session.invalidate();
		
		// セッションを取得、セッションが存在しない場合はnull
		System.out.println(request.getSession(false));

		// セッションを取得、セッションが存在しない場合は新規作成
		session = request.getSession(true);

		// 新しいセッションIDが作られている
		System.out.println(session.getId());

	}
}

<コンソールの出力例>

5E5B0799298D6A98A56BFCBCAF4FAEFF
exampleUser
null
5E5B0799298D6A98A56BFCBCAF4FAEFF
null
DAF55E879825AFB4A3CEE6E712D1E7E2

例題

セッション属性を使いこれまで作成してきた数当てゲームを改良して複数回のチャレンジが可能なようにしなさい。

また、「もう一度プレイする」のリンクをクリックすることで再度プレイできるようにしなさい。

各チームで新プロジェクトの名前を決めて作成すること。

セッション属性を使った数当てゲーム
セッション属性を使った数当てゲーム

使用ファイル:index.jsp, GameServlet.java, Kazuate.java, correct.jsp(答えが正しかった場合), result.jsp(答えが正しくなかった場合),その他必要に応じて追加すること。

なお、エラー処理は後回しにしてよい。

また、複数のブラウザから同時に実行して以下のようにそれぞれ違った答えになることを確認すること。

セッション属性
複数のブラウザで結果の検証

例題

7章を学んだあとで上記例題5から<c:if>タグを使い、不要なHTML要素を削除しなさい。

不要な要素を消した数当てゲーム
不要な要素を消した数当てゲーム

今回はログイン処理の実装方法を通じてセッションについて学びました。

Webページというものは本来1ページ、1ページが独立していて、ページをまたいでデータを引き継ぐ仕組みがありません。このようにhttp通信のリクエストとレスポンスがそれぞれ独立していて他の http通信にデータを引き継がないことをステートレス【stateless】といいます。【state】= 状態 が【less】=(引き継げ)ないというわけでしたね。

本来http通信はステートレスであり、一対のリクエストとレスポンスで通信は終了します。しかし、複数のリクエストとレスポンスを同じものとして扱うことができなければ、ログイン処理や買い物かごの処理は実現できません。ページをまたぐごとにユーザーを忘れてしまったり、買物かごの中身が消えてしまったら使い物になりませんね。

そこで必要となるのがセッションです

Webアプリケーションは、本来ステートレスとして作られたhttp通信を使って擬似的にステートフルにしてオンライン商取引などに使っているわけです。そのため、かなり無理をしている(?)感じは否めないかと思います。

ブラウザの表示がおかしいときは?

Webシステムを開発しているとキャッシュが邪魔をして想定外の動きになることがあります。そんな時は以下の対策を試してみてください。

  • ブラウザのスーパーリロード(Ctrl + Shift + F5)を試す
  • ブラウザを再起動する(すべてのタブを閉じることでセッションが消える)
  • サーバーのタブからTomcatを「停止」して「クリーン」してから再度実行する
  • 別のブラウザで実行する(例えば普段Edgeを使って開発している場合はChromeで実行する)
  • ブラウザのゲストモードで実行する(設定方法は以下のとおりですが、詳しくは講師がお話しします)
セッション属性
ゲストモードで実行する場合の設定方法

Eclipseのクリーン起動

Eclipseのクリーン起動(Clean Start)は、Eclipse統合開発環境(IDE)を特定の状態で起動するための方法です。通常、Eclipseは前回のセッションの状態を記憶しており、起動時に前回の状態を復元します。しかし、クリーン起動を行うと、前回の状態やキャッシュを無視して、新しい状態でEclipseを起動することができます。

クリーン起動は通常eclipse.exeと同じフォルダに入っています。「eclipse.exe -clean.cmd」アイコンをダブルクリックします。

セッション属性
Eclipseのクリーン起動

「ポート番号を変更する必要があります」となったときは?

これは、何らかの原因でtomcatが終了していない間に新しくtomcatを起動したことが原因です。

セッション属性
サーバーが2重で起動する場合の問題

tomcatが入っているフォルダの中に「kill-8080-port.cmd」というツールがありませんか?

セッション属性
kill-8080-port.cmd

これをダブルクリックすることでサーバーをkillすることができ、問題解決につながります。

「Tomcatxxに問題が発生しました。Tomcatxxは起動に失敗しました。」となったときは?

セッション属性

対策① まずはこの方法を試す。

  • サーバービューを開く:画面下部に「サーバー」ビューが表示されていない場合は、メニューバーから「ウィンドウ」>「ビューの表示」>「サーバー」を選択します。
  • Tomcatサーバーを選択:「サーバーズ」ビューに表示されているTomcatサーバー(例:localhostのTomcat9)を見つけてクリックします。
  • サーバー構成を開く:Tomcatサーバーの左側にある「>」マークをクリックして展開します。現在のサーバーにデプロイされている全てのアプリケーションがリスト表示されます。
  • アプリケーションを除去:除去したいアプリケーションを右クリックして「除去」を選択します。
  • サーバーを保存して再起動:サーバーエディタの上部にある「保存」ボタン(ディスクアイコン)をクリックして変更を保存します。変更を反映させるために、サーバーを再起動します。これを行うには、「サーバー」ビューでTomcatサーバーを右クリックして「リスタート」を選択します。

上記で改善しない場合は、urlPatternsが重複したサーブレットがあります。

セッション属性

対策② ワークスペースを検索して同じurlPatternsを見つけ、どちらか一方を書き換える必要があります。

  • 検索ダイアログを開く(Ctrl + H)
  • 「ファイル検索」タブを選択
  • 「含む文字列」フィールドに検索文字列を入力(例.@WebServlet)
  • 「スコープ」で「ワークスペース」を選択
  • 「検索」ボタンをクリック
セッション属性

上記の画面で同じurlPatterns(上記の例では/Game9)を解消してください。

<まとめ:隣の人に正しく説明できたらチェックを付けましょう>

□ ログイン処理を実現するにはセッションという仕組みを理解することが必要

□ セッション属性は同一ブラウザで開始してから破棄されるまで、同一ユーザー(ブラウザ)であれば全てのページで有効なスコープである

□ 画面遷移の方法にはフォワードとリダイレクトがある

□ フォワードはリクエスト属性を引き継ぐがリダイレクトは引き継がない(ただし、リクエストパラメータを付加することは可能)

□ 「リクエスト属性とフォワード」、「セッション属性とリダイレクト」はセットであると考えてよい。

□ request.getSession(true) と記述することでセッションが既に存在する場合はそのセッションを返し、存在しない場合は新規にセッションを作成する。

□ request.getSession(false) と記述することでセッションが既に存在する場合にのみ、そのセッションを返し、存在しない場合は null を返す。

□ セッション属性へのオブジェクトの出し入れのメソッド名はリクエスト属性のときと同じgetAttribute/setAttributeである

□ http通信はステートレスなためページをまたいでデータを保持するにはセッションが必要である

次回は、JavaBeansを学んでいきます。

JavaBeansを使うことで今回のセッション属性にオブジェクトをまるごと入れて複数ページをまたいだ処理が可能になります。例えば、買物かごのような仕組みも実現できるようになります。

「セッション属性を使いログインや買い物かごの仕組みを実装する」 最後までお読みいただきありがとうございます。