サーブレットの様々なサンプル - セッションの管理 -
技術情報 TOP へ

ここでは、Java サーブレットに関する簡単なサンプルを記述いたします。

( 1 ) セッションの生成
( 2 ) セッションの利用 - セッションへの値の格納と取得 -
( 3 ) セッションの破棄
( 4 ) セッションタイムアウトの設定
( 5 ) URL リライティング
( 6 ) セッションの生成、破棄時のイベント - セッションカウンタの作成 -



セッションについて

Java サーブレットは HTTP プロトコルを使用します。HTTP はステートレスプロトコルです。
サーバは、リクエストを受け取ってレスポンスを返すと、クライアント ( ブラウザ ) との接続を終了します。
サーバにはクライアントを一意に識別できる情報を持っていないため、クライアントの情報は維持されません。
( サーバはどのクライアントがどの情報を持っていたかを判断できない。 )

上記の問題を解決するための仕組みがセッション管理となります。

セッションとは、簡単に説明するとクライアントがサーバに接続してから切断するまでの 1 接続単位です。
一般的には 1 リクエスト単位ではなく、例えば、ユーザがログインしてからログアウトもしくは、
ブラウザを閉じるなどの操作を行うまでの間を表します。

1 セッションの間には、複数回のリクエスト送信、レスポンス受信が行われます。上述したように、
HTTP はステートレスプロトコルですので、どのリクエストがどのユーザのものなのかを
判断する一意の情報が必要です。この一意に識別される情報をセッション ID と言います。

セッション ID は不規則な英数字の組合せを 10 数桁 以上 ( アプリケーションサーバによって相違が
あります。 ) の組合せで作成され、このセッション ID をサーバは初回アクセス時にクライアントに
送信し、クライアントは受け取ったセッション ID をリクエスト時にいっしょに送信します。

上記の仕組みからサーバはクライアントを一意に識別することができ、1 セッションの間、
クライアントの情報を保持することができます。( セッション間で有効な変数などを利用できる。 )

サーバ、クライアント間の情報を保持する仕組みでクッキーとセッションの違いは、クッキーは
クライアント側での情報の保持であり、セッションはサーバ側での情報の保持となります。
( セッションのセッション ID の保持はクッキーを利用しています。 )

通常、セッション ID はクッキーに保存されます。クライアントのクッキーが利用できない場合、
URL リライティング ( URL に セッション ID を含める ) を行います。

セッション管理を行う上で以下の点に留意します。

セッションタイムアウトを適切に設定します。

ブラウザから何もリクエストが来ない状態がつづくと,サーバ側ではユーザが自サーバのページを閲覧中なのか、
アクセスを中止してしまったのか ( ブラウザを閉じるなど ) を知ることができません。

このような場合、サーバ側がブラウザからのリクエストをいつまでも待ち続けるのはシステム資源やセキュリティ的な観点
から得策ではありません。

一定時間リクエストが無かったセッションは切断する必要があります。


使用済みのセッションは必ず破棄します。

ユーザがログアウトなどの操作 ( 以降、継続したアクセスを行わない、システム資源を使用しない。 ) を行った場合は、
システム資源やセキュリティ的な観点からも必ず、セッションの破棄を行う必要があります。


クラスタリング環境では、セッションに格納するオブジェクトはシリアライズ可能である必要があります。

クラスタリング環境でセッションに格納するオブジェクトは全て ( 独自に作成した JavaBeans も含む )
java.io.Serializable が実装されている必要があります。

クラスタリングの処理では、セッションオブジェクトを一旦 byte 配列に変換して各サーバに送信します。
受信側は、受け取った byte 配列からオブジェクトを再生成します。
この処理では、各オブジェクトがシリアライズ可能になっている必要があります。

クラスタリング環境で java.io.Serializable が実装されていないオブジェクトをセッションに格納しようとすると
以下のようなエラー内容が出力されます。

java.lang.IllegalArgumentException: Attribute [オブジェクト名] is not serializable


セッションの有無だけでログインしていることを判断すると二重ログインが発生する可能性があります。

ブラウザで接続中に、別のブラウザでアクセスするとセッションは別になります。


通常、セッション ID はクッキーに保存されますが、クッキーは HTTP 環境ではテキストです。

セッション ID が盗まれる ( セッションハイジャック ) 可能性を持ちます。


初回アクセス時はクライアント側でクッキーが利用できるか否か不明のため、 URL リライティングを合わせて行うことも多く、その際、URL に付与されるため、 セッション ID はユーザに見える状態になることがあります。



Java サーブレットでは、セッション管理に HttpSession というインターフェイスを使用します。

アプリケーションサーバはクライアントに対応する HttpSession のオブジェクトを簡単な
データベースで管理していて、クライアントからのリクエストに含まれるセッション ID が
このデータベースに存在した場合、そのユーザはセッションを持っていると判断し、既存の
セッションを返します。

セッション ID を持っていない場合や HttpSession のオブジェクトを簡単なデータベースに
存在しない場合、セッションを持っていないと判断し、新規のセッションが生成されます。



( 1 ) セッションの生成

リクエストを送信してきたブラウザに対して、セッションの生成を行いセッションを開始します。

上述のようにセッションはセッション ID によって識別されます。セッション ID は通常、クッキーに 保存されます。

* ブラウザに Internet Explorer を使用されている場合は、[ツール] - [インターネットオプション] - [プライバシー]
- [詳細設定] - [自動 Cookie 処理を上書きする] にチェックを入れ、[ダイアログを表示する]を選択すると
クッキー が送信されてくる様子を確認できます。

ファイル名:SessionServlet.java
import java.io.*;
import javax.servlet.http.*;
import java.io.IOException;
import javax.servlet.ServletException;

public class SessionServlet extends HttpServlet {
		  
	private static final long serialVersionUID = 1L;

	public void doGet (HttpServletRequest request, HttpServletResponse response)
	  	throws ServletException, IOException {
		
	  /* getSession(false) は現在セッションが存在しなければ
	   *  null を返却します。 */	
	  HttpSession session=request.getSession(false);
	  
	  response.setContentType("text/html; charset=shift_jis");
	
	  PrintWriter out = response.getWriter();
	  
	  out.println("<html>");
	  out.println("<head>");
	  out.println("<title>Cookie Example</title>");
	  out.println("</head>");
	  out.println("<body>");
	  	 
	  out.println("<h2>");
	  
	  if(session == null){
		  out.println("セッションが存在しません。");
		  /* getSession(true) でセッションが存在しなければ
		   * 新規セッションを作成します。 */
		  session=request.getSession(true);
	  }
	  else{
		  out.println("セッションが存在します");
	  }
	  
	  out.println("</h2>");
	  out.println("<BR>");	  
	  out.println("<form method=\"GET\" action=\""
	    + request.getContextPath() + "/SessionServlet\">");  
	  out.println("<input type=\"submit\" value =\"次 へ\"> ");  
	  out.println("</form>");  	  
	  out.println("</body>");
	  out.println("</html>");
	  out.close();
	}
}



( 2 ) セッションの利用 - セッションへの値の格納と取得 -

セッションに String オブジェクトを格納して、別のリクエストから取得してみます。

ファイル名:CookieServlet.java
import java.io.*;
import javax.servlet.http.*;
import java.io.IOException;
import javax.servlet.ServletException;

public class SessionServlet extends HttpServlet {
		  
	private static final long serialVersionUID = 1L;

	public void doGet(HttpServletRequest request, 
                      HttpServletResponse response) 
                          throws ServletException, IOException {

	String tex = "Hello!";
		
	/* セッションを作成します。 */			
	HttpSession session=request.getSession(true);
	
	/* tex をセッションへ格納します。 */
	session.setAttribute("sessionKey1",tex);
		  
	/* ContentType を設定*/
	response.setContentType("text/html; charset=Shift_JIS");

	/* 出力用 PrintWriter を取得*/
	PrintWriter out = response.getWriter();
	    
	/* HTML 出力 */
	out.println("<html>");
	out.println("<head>");
	out.println("<title>Session Sevlet GET</title>");
	out.println("</head>"); 
	out.println("<body>");   
	out.println("<h2>セッションに値を設定しました。</h2>"); 
	out.println("<h2>" + tex + "</h2>"); 
	out.println("<form method=\"POST\" action=\""
	 		+ request.getContextPath() + "/SessionServlet\">");  
	out.println("<input type=\"submit\" value =\"POST送信\">" );  
	out.println("</form>");   
	out.println("</body>");
	out.println("</html>");	
	}

	public void doPost (HttpServletRequest request,
		HttpServletResponse response)
		throws ServletException, IOException {
			
	/* ContentType を設定*/
	response.setContentType("text/html; charset=Shift_JIS");

	/* 出力用 PrintWriter を取得*/
	PrintWriter out = response.getWriter();
	    
	/* HTML 出力 */
	out.println("<html>");
	out.println("<head>");
	out.println("<title>Session Sevlet POST</title>");
	out.println("</head>"); 
	out.println("<body>");   
	out.println("<h2>セッションから値を取り出しました。</h2>"); 
	
	/* getAttribute を使用してセッションより値を取得 */
	out.println("<h2>" 
	  + request.getSession(true).getAttribute("sessionKey1") + "</h2>");

	out.println("<form method=\"POST\" action=\""
	  + request.getContextPath() + "/SessionServlet\">");  

	out.println("<input type=\"submit\" value =\"POST送信\"> ");  
	out.println("</form>");  
	out.println("</body>");
	out.println("</html>");    
	}
 } 



( 3 ) セッションの破棄

セッションを破棄するには、以下の invalidate メソッドを使用します。

プログラムの例:セッションを破棄する処理
	  /* getSession(false) は現在セッションが存在しなければ
	   *  null を返却します。 */	
	  HttpSession session=request.getSession(false);

	  /* セッションが存在すれば破棄します。 */
	  if(session != null){
	    /* セッションsessionを破棄 */
	    session.invalidate();
	  }



( 4 ) セッションタイムアウトの設定

セッションタイムアウトは web.xml もしくは プログラムから設定できます。
両方設定した場合は、プログラムからの設定が優先されます。
( web.xml の設定をデフォルト値として、プログラムからの設定をセッションごとの設定とすることも可能です。 )

web.xml での設定

web.xml でセッションタイムアウトを設定するには、 web.xml の session-config 要素 ( <session-config> ) 内の session-timeout 要素 ( <session-timeout> ) を記述し値を設定します。
( 単位は分です。)

web.xml の記述例:セッションタイムアウトを 20 分に設定
<web-app> 
	<!--ここから-->
	<session-config>
	  <session-timeout>20</session-timeout>   
	</session-config>
	<!--ここまで-->
プログラムでの設定

プログラムでセッションタイムアウトを設定するには、 setMaxInactiveInterval メソッドを使用します。
( 単位は秒です。)

プログラムの例:現在のセッションのタイムアウトを 20 分に設定
HttpSession session = request.getSession(true);
session.setMaxInactiveInterval(1200);



( 5 ) URL リライティング

クライアント側でクッキーが利用できない場合、 URL にセッション ID を付与してクライアント - サーバ間の
セッションを維持します。 このような方式を URL リライティングと言います。

URL リライティングを使用すると、セッション ID がユーザにも簡単に確認できてしまう問題はありますが、
システムがクッキーが使用できない環境にも対応しなければならない場合は有効なセッション維持の方法となります。

( URL リライティングによるセッション維持を行う場合、サーブレットや JSP などからシステムで使用する
URL すべてでURL リライティングを行う必要があります。漏れがあるとセッションが途切れることになります。)

encodeURL メソッドの処理について

URL リライティングを行う場合、HttpServletResponse インタフェース の encodeURL メソッドを使用します。

まず、encodeURL メソッドでは、ブラウザのクッキーの利用の可否を判定します。
( クッキーが利用可能であればセッション ID は付与させません。 )

次に、引数 url のプロトコル、ホスト、ポート、コンテキストパス等のチェックを行います。

このチェックにて、プロトコル、ホスト、ポート、コンテキストパス等が HttpServletRequest#getScheme()、
getServerName()、getServerPort()、getContextPath() の値と同一である場合、
セッションID が付与されます。一つでも一致しない場合、セッションIDは付与されません。

HttpServletResponse- encodeURL

HttpServletResponse インターフェイスの実装クラスである HttpServletResponseWrapper は下記の
ドキュメントにもありますとおり、ラップされたレスポンス・オブジェクトのメソッドを呼び出します。

HttpServletResponseWrapper

筆者が確認したところ、Tomcat 5.0.* 系での HttpServletResponseWrapper のレスポンス・オブジェクトは、
org.apache.coyote.tomcat5.CoyoteResponse でした。



encodeURL メソッド を使用して URL リライティングを行います。

ファイル名:SessionServlet.java
import java.io.*;
import javax.servlet.http.*;
import java.io.IOException;
import javax.servlet.ServletException;

public class SessionServlet extends HttpServlet {
		  
	private static final long serialVersionUID = 1L;

	public void doGet (HttpServletRequest request, HttpServletResponse response)
	  	throws ServletException, IOException {
		
	  /* getSession(false) は現在セッションが存在しなければ
	   *  null を返却します。 */	
	  HttpSession session=request.getSession(false);	  
	  
	  response.setContentType("text/html; charset=shift_jis");
	
	  PrintWriter out = response.getWriter();
	  
	  out.println("<html>");
	  out.println("<head>");
	  out.println("<title>Session Example</title>");
	  out.println("</head>");
	  out.println("<body>");
	  	 
	  out.println("<h2>");
	  
	  if(session == null){
		  out.println("セッションが存在しません。");
		  /* getSession(true) でセッションが存在しなければ
		   * 新規セッションを作成します。 */
		  session=request.getSession(true);
	  }
	  else{
		  out.println("セッションが存在します");		  
	  }
	  
	  /* URL リライティングを行います。 */
	  String url= response.encodeURL(request.getContextPath() + "/SessionServlet");
	  
	  out.println("</h2>");
	  out.println("<BR>");	  
	  out.println("<form method=\"GET\" action=\""
	    + url + "\">");  
	  out.println("<input type=\"submit\" value =\"次 へ\"> ");  
	  out.println("</form>");  	  
	  out.println("</body>");
	  out.println("</html>");
	  out.close();
	}
}



( 6 ) セッションの生成、破棄時のイベント - セッションカウンタの作成 -

セッションが生成されるタイミング及び破棄されるタイミングでイベントを取得することができます。
HttpSessionListener を継承したクラスを作成し、sessionCreated メソッド及び sessionDestroyed メソッドを
オーバーライドして、web.xml の listener-class に設定します。

セッションの生成、破棄を利用して現在アクティブなセッションの数を数えます。

ファイル名:CheckSession.java
import javax.servlet.http.*;

/**
 * HttpSessionListener を継承したクラスを使用します。 
 */
public class CheckSession implements HttpSessionListener {
   /*
   * セッション数を設定する int 型変数
   */
  private static int count = 0;
  
  /**
   * セッションが生成されたタイミングで呼ばれます。
   */
  public void sessionCreated(HttpSessionEvent hse) {
	  count++;
	  System.out.println("sessionCreated Count " + count);   
  }
 
  /**
   * セッションが破棄されたタイミングで呼ばれます。
   */
  public void sessionDestroyed(HttpSessionEvent hse) {
	  count--;
	  System.out.println("sessionDestroyed Count " + count);    
  }
}
web.xml の設定

web.xml の listener 要素 ( <listener> ) 内の listener-class 要素 ( <listener-class> ) を記述し、 値に CheckSession クラスを設定します。

web.xml の記述例:CheckSession クラスを listener-class 設定
<web-app>
  <!--ここから-->
  <listener>
	  <listener-class>CheckSession</listener-class>
  </listener>
  <!--ここまで-->



技術情報 TOP へ


Google
WWW を検索 whitemark.co.jp を検索
[ 株式会社ホワイトマーク TOP ]   [ 免責事項 ]

Copyright © 2006 by WhiteMark, All rights Reserved. Last Modified: 2006/05