第1章 クッキーとセッションを利用しよう

1.4 セッションを利用してみよう

本節ではスコープの1つであるセッションの使い方について学習していきます。

1.4.1 データの登録と取得

セッションにデータを登録する場合、まず始めにセッションオブジェクトを生成します。

HttpServletRequestインタフェースに定義されたgetSession()メソッドを利用し、HttpSessionオブジェクトを取得します。

HttpSessionインタフェースのオブジェクトに用意されたsetAttribute()メソッドを利用することで、セッションへデータを登録することができます。メソッドの引数には、登録するデータとそのデータにつける名前を渡します。

HttpSessionインタフェースのオブジェクトに用意されたgetAttribute()メソッドを利用することで、セッションに登録されたデータを取得することができます。メソッドの引数には、登録されたオブジェクトの名前を渡します。リクエストスコープと同じように、登録されたデータはObject型となるため、セッションから取得したデータは登録する前の型でキャストを行う必要があります。

では実際にセッションを利用するプログラムを作成してみましょう

セッションを利用しサーブレットからJSPへデータを渡すプログラム
実行結果

アプリケーション構成

【ファイル名 :SessionController.java】
package jp.co.f1.spring.session;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.ModelAndView;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
@Controller
public class SessionController {
	/**
	 * 「useSession」へGET送信された場合
	 */
	@GetMapping("/setSession")
	public ModelAndView setSession(ModelAndView mav, HttpServletRequest request, HttpServletResponse response) {
		//セッションオブジェクトの生成
		HttpSession session = request.getSession();
		//セッションへのデータの登録
		session.setAttribute("bookTitle", "Springの本");
		//リダイレクト先を指定
		mav = new ModelAndView("redirect:/getSession");
		// ModelとView情報を返す
		return mav;
	}
	/**
	 * 「useSession」へPOST送信された場合
	 */
	@GetMapping("/getSession")
	public ModelAndView getSession(ModelAndView mav, HttpServletRequest request, HttpServletResponse response) {
		//セッションオブジェクトの生成
		HttpSession session = request.getSession();
		//セッションへのデータの取得
		String title = (String) session.getAttribute("bookTitle");
		//セッションから取得したデータをModelに格納
		mav.addObject("bookTitle", title);
		// 画面に出力するViewを指定
		mav.setViewName("getSession");
		// ModelとView情報を返す
		return mav;
	}
}
【getSession.html】
<html>

<head>
  <title>セッションを利用したプログラム</title>
</head>

<body>
  <span th:text="${bookTitle}"></span><br>
</body>

</html>
アプリケーションにアクセス

以下のアドレスからHelloWorldアプリケーションにアクセスします。
URL:http://localhost:8080/setSession

解説

setSessionメソッドではセッションにデータを登録します。

まず、HttpSessionインタフェースのオブジェクトを生成しています。セッションオブジェクトの生成にはnew演算子を利用するのではなくrequestオブジェクトのgetSession()メソッドを利用します。

HttpSession session = request.getSession();

続いて、実際にデータをセッションに登録しています。setAttribute()メソッドの第1引数に「bookTitle」という文字列を渡し、第2引数には「Springの本」という文字列を渡しています。リクエストスコープを利用するプログラムと異なる点は、メソッドを呼び出すためのオブジェクトが違うということだけで、メソッドの使い方自体は全く変わりません。

session.setAttribute("bookTitle", "Springの本");

このときにセッションへ登録されるデータも、リクエストスコープと同様にObject型として登録されます。

その後、リダイレクト先を指定します。

mav = new ModelAndView("redirect:/getSession");

getSessionメソッドへの処理の転送後は、getAttributeメソッドを使うタイミングで登録された文字列を取得しています。

getAttribute()メソッドの引数に「bookTitle」という文字列を渡しています。この引数の文字列がセッションから取得するデータの名前になります。このデータの取得方法についても、リクエストスコープとの違いはメソッドを呼び出すためのオブジェクトが違うという点だけで、メソッドの使い方自体は全く変わりません。

String title = (String) session.getAttribute("bookTitle");

リクエストスコープと同様に、登録されたデータはObject型で取得されます。そのため、取得されたデータを適切に扱うためには、型をセッションへ登録される前の型に明示的に変換する必要があります。

実行結果からわかるように、サーブレット内でセッションに登録した文字列をJSPで表示することができています。このように、リクエストスコープと同じく、セッションを使った場合でも複数のファイル間でデータの共有が行えます。

1.4.2 セッションの破棄

セッションに登録されたデータは、接続が続いている間保持されます。しかし、状況によってはセッションのデータを削除したい場合があります。そのような場合は、セッションの破棄を行います。

Sessionオブジェクトのinvalidate()メソッドを利用することで、セッションを破棄することができます。

では、セッションのデータを破棄するプログラムを作成してみましょう。

セッションのデータを破棄するプログラム

入力フォームから入力されたデータをセッションに登録し、別の画面に表示します。表示用のJSPページから入力フォームへ戻る際、任意でセッションデータを破棄することができ、破棄しない場合は入力値をフォーム内に引き継ぎます。また、破棄した場合はフォーム内が空の状態で表示することができます。

実行結果
アプリケーション構成
【ファイル名 :SessionController.java】(以下を追記)
/**
 * 「useSessionForm1」へGET送信された場合
 */
@GetMapping("/useSessionForm1")
public ModelAndView userSessionForm1(ModelAndView mav, HttpServletRequest request) {
	//セッションオブジェクトの生成
	HttpSession session = request.getSession();
	//セッションがある場合、情報を取得
	if(session != null) {
		//セッションへのデータの取得
		String name = (String) session.getAttribute("name");
		String age = (String) session.getAttribute("age");
		String sex = (String) session.getAttribute("sex");
		// 画面に出力するViewを指定
		mav.addObject("name", name);
		mav.addObject("age", age);
		mav.addObject("sex", sex);
	}
	// 画面に出力するViewを指定
	mav.setViewName("useSessionForm1");
	// ModelとView情報を返す
	return mav;
}
/**
 * 「useSessionForm2」へGET送信された場合
 */
@GetMapping("/useSessionForm2")
public ModelAndView userSessionForm2(ModelAndView mav, HttpServletRequest request) {
	//セッションオブジェクトの生成
	HttpSession session = request.getSession();
	//フォームに入力された値を取得
	String name = request.getParameter("name");
	String age = request.getParameter("age");
	String sex = request.getParameter("sex");
	//セッションへのデータの登録
	session.setAttribute("name", name);
	session.setAttribute("age", age);
	session.setAttribute("sex", sex);
	//リダイレクト先を指定
	mav = new ModelAndView("redirect:/useSessionForm3");
	// ModelとView情報を返す
	return mav;
}
/**
 * 「useSessionForm3」へGET送信された場合
 */
@GetMapping("/useSessionForm3")
public ModelAndView userSessionForm3(ModelAndView mav, HttpServletRequest request) {
	//セッションオブジェクトの生成
	HttpSession session = request.getSession();
	//セッションへのデータの取得
	String name = (String) session.getAttribute("name");
	String age = (String) session.getAttribute("age");
	String sex = (String) session.getAttribute("sex");
	// 画面に出力するViewを指定
	mav.addObject("name", name);
	mav.addObject("age", age);
	mav.addObject("sex", sex);
	// 画面に出力するViewを指定
	mav.setViewName("useSessionForm2");
	// ModelとView情報を返す
	return mav;
}
/**
 * 「useSessionForm4」へGET送信された場合
 */
@GetMapping("/useSessionForm4")
public ModelAndView userSessionForm4(ModelAndView mav, HttpServletRequest request) {
	//セッションオブジェクトの取得
	HttpSession session = request.getSession();
	//セッションがある場合、セッションを破棄
	if(session != null) {
		session.invalidate();
	}
	// 画面に出力するViewを指定
	mav.setViewName("useSessionForm1");
	return mav;
}
【ファイル名:useSessionForm1.html】
<html>

<head>
  <title>セッションを破棄するプログラム</title>
</head>

<body>
  <form action="/useSessionForm2">お名前:
    <input type="text" name="name" th:value="${name}">
    <br>年齢:
    <input type="text" name="age" th:value="${age}">
    <br>性別:
    <div th:if="${sex == null or sex == '男性'}">
      <input type="radio" name="sex" value="男性" checked>男性
      <input type="radio" name="sex" value="女性">女性
      <br>
    </div>
    <!-- 女性の場合 -->
    <div th:if="${sex == '女性'}">
      <input type="radio" name="sex" value="男性">男性
      <input type="radio" name="sex" value="女性" checked>女性
      <br>
    </div>
    <input type="submit" value="送信">
  </form>
</body>

</html>
【ファイル名:useSessionForm2.html】
<html>

<head>
  <title>セッションを破棄するプログラム</title>
</head>

<body>お名前:
  <span th:text="${name}">
  </span>
  <br>年齢:
  <span th:text="${age}">
  </span>
  <br>性別:
  <span th:text="${sex}">
  </span>
  <br>
  <form action="/useSessionForm4">
    <input type="submit" value="セッションを破棄して戻る">
  </form>
  <form action="/useSessionForm1">
    <input type="submit" value="セッションを破棄せず戻る">
  </form>
</body>

</html>
アプリケーションにアクセス

以下のアドレスからアプリケーションにアクセスします。
URL:http://localhost:8080/useSessionForm1

解説

このプログラムは、フォームから入力されたデータをセッションに登録し画面に一覧で表示しています。

表示用の画面には「セッションを破棄して戻る」と「セッションを破棄せず戻る」の2つのボタンがあり、どちらもフォーム画面を表示しますが、「セッションを破棄して戻る」を選択した場合は、セッションに登録された入力データが全て削除されます。また、「セッションを破棄せず戻る」を選択した場合は、入力したデータをフォーム画面に引き継いて表示します。

最初にアクセスされるuseSessionForm1メソッドでは、セッションデータを取得しフォーム部品の値として設定しています。セッションデータがない場合は、各フォーム部品の値を空白に設定しています。

if(session != null) {
	//セッションへのデータの取得
	String name = (String) session.getAttribute("name");
	String age = (String) session.getAttribute("age");
	String sex = (String) session.getAttribute("sex");
	// 画面に出力するViewを指定
	mav.addObject("name", name);
	mav.addObject("age", age);
	mav.addObject("sex", sex);
}

また、HTML内にif文を埋め込むことで、ラジオボタンのチェック選択部分を動的に設定しています。

性別:
  <div th:if="${sex == null or sex == '男性'}">
    <input type="radio" name="sex" value="男性" checked>男性
    <input type="radio" name="sex" value="女性">女性
    <br>
  </div>
  <!-- 女性の場合 -->
  <div th:if="${sex == '女性'}">
    <input type="radio" name="sex" value="男性">男性
    <input type="radio" name="sex" value="女性" checked>女性
    <br>
  </div>

この設定によって、入力されたデータを破棄しない場合はデータを引き継ぐことができます。

入力されたデータを表示するuseSessionForm2.htmlでは2種類のボタンによって転送先を変えています。

「セッションを破棄して戻る」を選択した場合はUseSessionForm4メソッド、「セッションを破棄せず戻る」を選択した場合はuseSessionForm1メソッドへ転送されます。

<form action="/useSessionForm4">
  <input type="submit" value="セッションを破棄して戻る">
</form>
<form action="/useSessionForm1">
  <input type="submit" value="セッションを破棄せず戻る">
</form>

「セッションを破棄して戻る」が選択された場合、UseSessionForm4メソッドへ転送された後、最初のフォーム画面へ転送されます。セッションの破棄はこのUseSessionForm4メソッドで行われています。

UseSessionForm4メソッドではセッションの破棄が行われます。

まずセッションオブジェクトを取得し、if文でセッションオブジェクトが存在するかどうかを判定しています。セッションオブジェクトがすでにnullになっていた場合、破棄しようとするとエラーになってしまうため、注意が必要です。if文内ではinvalidate()メソッドを利用してセッションを破棄します。

//セッションがある場合、セッションを破棄
if(session != null) {
	session.invalidate();
}

UseSessionForm4メソッド内でセッションが破棄されると、最初のフォーム画面に戻った際にセッションのデータを取得できなくなるため、何もデータが入力されていない状態に戻る仕組みになっています。

このように、セッションの破棄を行うことでセッション内のデータを全て削除することができます。

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

ショッピングサイトなどで、ログインを行った後に何もせずに時間が経過したとき、「接続の有効時間が切れました。再度ログインしてください。」というようなメッセージが表示されることがあります。

セッションのデータは同一の接続が続いている間保持されますが、サーバー側では、実際にブラウザが閉じられたかどうかを正確に知る方法がありません。そのため、最後のアクセス時間からの経過時間をチェックし、一定時間が過ぎた場合に、セッションを削除する機能が備わっています。この仕組みのことをセッションタイムアウトと呼びます。

このセッションタイムアウトには主に2つの目的があります。

1つ目は、サーバーのリソースを効率的に利用することです。 セッションのデータは接続ごとに管理されるため、接続数が増え過ぎるとリソースが不足し、サービスの提供ができなくなる恐れがあります。そこで、セッションタイムアウトによって、システムを一定時間利用していないユーザーのセッションを削除しリソースを解放します。

2つ目は、セキュリティ対策です。ショッピングサイトなどでログインしたまま長時間PCから離れた場合、第三者がその PC を不正に利用し個人情報などが盗まれてしまう可能性があります。そのようなことを防ぐため、セッションタイムアウトを短めに設定することができます。

セッションタイムアウトの設定は、web.xmlというWebアプリケーションの各種設定を行うことができるファイルに、<session-config>要素の子要素である<session-timeout>で指定することができます。

上記の書式ではセッションタイムアウトを1分に指定しています。

なお、Tomcat10の場合、セッションタイムアウトのデフォルトは30分となっています。

では、セッションタイムアウトを設定し、その時間でセッションが削除されることを以下の手順に沿って確認してみましょう。

Step1:application.propertiesファイルへの記述
Step2:動作確認

Step1:application.propertiesファイルへの記述

以下のようにapplication.propertiesへ記述してみましょう。

Step2:動作確認

① 確認用のWebアプリケーションにアクセス

セッションタイムアウトの確認には前項で作成したプログラムを利用します。
「実行」ボタンからアプリケーションを起動し、URLにアクセスしましょう。
URL:http://localhost:8080/useSessionForm1

② セッションデータを登録しタイムアウトまで待機

名前、年齢、性別を入力し、送信ボタンをクリックします。

その後、表示されるデータの一覧画面で1分間何もせずに待ちます。

③ 「セッションを破棄せず戻る」クリック

1分経過したら「セッションを破棄せず戻る」ボタンをクリックします。

セッションタイムアウトによってセッションが削除され、入力フォームの値が消えているのが確認できます。

NEXT>> 第2章 テーブル同士を連携しよう