クッキーのセッションID削除とTomcatのセッション破棄したときの動きの違い

クッキーのセッションIDを削除したときと、アプリケーションサーバー(Tomcat)が持つセッションを破棄したときの動きの違いを確かめてみました。

実行環境
OS: ubuntu 20.04 LTS
Java: OpenJDK 1.8
Tomcat: ver 9.0.65

クッキーの内容とセッションIDを表示するServletです。

※テキストエディタの画像を貼り付けたのでソースコードは最後に記載しておきます。

これをブラウザで実行したときの結果です。初回アクセスのためクッキーにはまだセッションIDが設定されていません。Tomcatが発行したセッションIDは表示されています。

もう一度、同じURLにアクセスします。クッキーにも同じセッションIDが設定されました。

クッキーの内容を削除するServletです。

※ソースコードは最後に記載しておきます。

クッキーの削除ですが、クッキーの生存期間をゼロにすることで削除します。

ブラウザで実行したときの結果です。クッキーに設定されいていたJSESSIONIDが削除されました。

クッキーの内容とセッションIDを表示させてみます。

クッキーに設定されているセッションIDを削除したので新しいセッションIDになるのかと思ったのですが、そうでもないようです。削除したはずのセッションIDがクッキーに設定されていました。おそらくですが、Tomcatが管理しているセッションIDをクッキーに再設定していると思われます。

セッションを破棄するServletです。

※ソースコードは最後に記載しておきます。

ブラウザで実行したときの結果です。セッションが削除されました。

クッキーの内容とセッションIDを表示させてみます。クッキーは前回と同じセッションIDとなっていますが、Tomcatが管理するセッションは新しいIDとなっています。

もう一度、同じURLにアクセスします。

クッキーのセッションIDも新しいものになりました。クッキーが保持していたセッションIDはすでに破棄されたものであるため、新しいセッションIDをTomcatがクッキーに再設定したようです。

ちなみにですが、Tomcatのセッションの保持期間はweb.xmlに記載します。session-configタグのsession-timeoutに分単位で記載します。デフォルトでは30分です。session-timeoutに-1を設定するとセッションは無期限で保持されます。


<web-app ..
	:
	<session-config>
		<session-timeout>30</session-timeout>
	</session-config>
	:
</web-app>

使用したソースを載せておきます。

Show.java


package sample4;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.*;
import javax.servlet.annotation.WebServlet;

@WebServlet(urlPatterns={"/sample4/show"})
public class Show extends HttpServlet {

	public void doGet (
		HttpServletRequest request, HttpServletResponse response
		) throws ServletException, IOException {
	
		response.setContentType("text/html; charset=UTF-8");
		PrintWriter out = response.getWriter();

		out.println("<!DOCTYPE html>");
		out.println("<html>");
		out.println("<head>");
		out.println("<meta charset='UTF-8'>");
		out.println("<title>Show</title>");
		out.println("</head>");
		out.println("<body>");
		
		out.println("クッキーの表示<br>");		
		Cookie[] cookies = request.getCookies();
		if (cookies != null) {
			for (Cookie cookie : cookies) {
				String name = cookie.getName();
				String value = cookie.getValue();
				out.println(name + " : " + value + "<br>");
			}
		}
		
		out.println("セッションの表示<br>");
		HttpSession session = request.getSession();
		out.println("Session ID : "+ session.getId() +"<br>");

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

CookieRemove.java


package sample4;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.*;
import javax.servlet.annotation.WebServlet;

@WebServlet(urlPatterns={"/sample4/cookieremove"})
public class CookieRemove extends HttpServlet {

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

		response.setContentType("text/html; charset=UTF-8");	
		PrintWriter out = response.getWriter();

		out.println("<!DOCTYPE html>");
		out.println("<html>");
		out.println("<head>");
		out.println("<meta charset='UTF-8'>");
		out.println("<title>Cookie Remove</title>");
		out.println("</head>");
		out.println("<body>");
		
		Cookie[] cookies = request.getCookies();
		if ( cookies != null ) {
			for ( Cookie cookie : cookies ) {
				cookie.setMaxAge(0);
				response.addCookie(cookie);
				out.println( cookie.getName() + "を削除<br>" );
			}
		}

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

SessionRemove.java


package sample4;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.*;
import javax.servlet.annotation.WebServlet;

@WebServlet(urlPatterns={"/sample4/sessionremove"})
public class SessionRemove extends HttpServlet {

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

		response.setContentType("text/html; charset=UTF-8");	
		PrintWriter out = response.getWriter();

		out.println("<!DOCTYPE html>");
		out.println("<html>");
		out.println("<head>");
		out.println("<meta charset='UTF-8'>");
		out.println("<title>Session Remove</title>");
		out.println("</head>");
		out.println("<body>");

		HttpSession session = request.getSession();
		session.invalidate();
		out.println( "Session ID を削除<br>" );

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

ubuntuのTomcatからMySQLに接続するServletを作る。

TomcatからMySQLに接続してテーブルの内容を表示するServletを作ります。TomcatとMySQLは同じサーバー(OSはubuntu)にあります。Servletを動かすためにはJDBCドライバの配置とデータソースの設定をする必要があります。

実行環境
OS: ubuntu 20.04 LTS
Java: OpenJDK 1.8
Tomcat: ver 9.0.65
MySQL: ver 8.0.30

事前の準備としてJDBCドライバを入手します。MySQLのダウンロードページに行きます。

「Connector/J」を選びます。

Select Operating System: に「Ubuntu Linux」を、Select OS Version: には「Ubuntu Linux 20.04」を選びます。ドライバのバージョンは 8.0.30 です。「Download」を押下します。

ログインかサインアップを促す画面となりますが、下部にある「No thanks, just start my download.」をクリックします。

ファイルがダウンロードされます。

ダウンロードしたファイルは mysql-connector-java_8.0.30-1ubuntu20.04_all.deb というdebパッケージです。このパッケージをubuntuにインストールします。

JDBCドライバのインストール
$ sudo apt install ./mysql-connector-java_8.0.30-1ubuntu20.04_all.deb

/usr/share/java 配下に mysql-connector-java-8.0.30.jar が配置されます。

ここからTomcatの設定に入ります。

Tomcatの設定はServletの内容と関連するため、Servletの情報を記載します(実際のソースは後半に書きます)。

ソースファイル名: DsSample.java
クラスファイル名: DsSample.class
コンテキストパス: test
パッケージ: sample1

Tomcatのwebapps配下にファイルを配置していきます。ディレクトリは作成します。薄い青色がデイレクトリ、薄いオレンジ色がファイルです。薄い灰色はソースの配置場所です。

まずは、JDBCドライバを配置します。

/usr/share/java 配下にある mysql-connector-java-8.0.30.jar を WEB-INF/lib 配下にコピーします。シンボリックリンクの作成だとドライバのロードができませんでしたのでコピーをします。

次に、Tomcatのデータソースの設定を行います。データソースはMySQLへの接続情報です。META-INF 配下の context.xml に記載します。context.xml は新規作成します。

MySQLへの接続情報
サーバー: localhost
データベース名: testdb
ユーザ名: test
パスワード: test

コマンドラインでMySQLに接続したときのログです。SHOHINテーブルを検索しました。

この場合の context.xml の記載内容です。

※テキストエディタの画像を貼り付けたのでコードは最後に記載しておきます。

context.xml のResourceタグの各属性の意味です。

name
データソースの名前です。任意の名前をつけます。jdbc/test としました。

auth
Containerを指定するとTomcatが認証の処理を行います。Applicationを指定するとアプリケーションが認証の処理を行います。

type
リソースのクラス名またはインターフェース名です。javax.sql.DataSource を指定します。

driverClassName
JDBCドライバのクラス名です。MySQLのver8.xの場合、com.mysql.cj.jdbc.Driver となります。

url
接続文字列です。MySQLの場合は「jdbc:mysql://サーバー名:ポート/データベース名」という書式です。デフォルトポートの場合はポートの記載を省略できます。

username/password
ユーザ名とパスワードです。

Servletのソースコード( DsSample.java )を見ておきます。文字コードはUTF-8で記載しています。

※テキストエディタの画像を貼り付けたのでソースコードは最後に記載しておきます。

InitialContextクラスのlookupメソッドを使ってデータソースを取得します。取得したデータソースは javax.sql.DataSource インターフェイスのオブジェクトです。lookupメソッドの引数のリソース名は、context.xml のデータソース名の前に java:/comp/env/ を付与して java:/comp/env/jdbc/test とします。

DsSample.javaではWebServletアノテーションを使ってServletのURLを指定しています。コンテキストパスが test、コンテキストルート以降のパスは /sample1/dssample です。

Servletをjavacコマンドでコンパイルします。

コンパイルする際には servlet-api.jar にクラスパスを通しておきます。servlet-api.jar はwebappsと同じ階層のlibディレクトリにあります。コンパイルしたクラスファイルを WEB-INF/classes 配下に持っていきます。

これでServletを動かす準備ができました。Tomcatを起動させます。今回の場合は以下のURLとなります。

http://localhost:8080/test/sample1/dssample

ブラウザにはこんな感じで表示されます。

最後にソースコードを載せておきます。

context.xml


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration>

<Context reloadable="true">
	<Resource
		name="jdbc/test"
		auth="Container"
		type="javax.sql.DataSource"
		driverClassName="com.mysql.cj.jdbc.Driver"
		url="jdbc:mysql://localhost/testdb"
		username="test"
		password="test"
	/>
</Context>

DsSample.java


package sample1;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.*;
import javax.servlet.annotation.WebServlet;
import javax.naming.InitialContext;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

@WebServlet(urlPatterns={"/sample1/dssample"})
public class DsSample extends HttpServlet
{
	public void doGet (
		HttpServletRequest request, HttpServletResponse response
		) throws ServletException, IOException
		{
			response.setContentType("text/html; charset=UTF-8");
			PrintWriter out = response.getWriter();

			try	{
				out.println("<!DOCTYPE html>");
				out.println("<html>");
				out.println("<head>");
				out.println("<meta charset='UTF-8'>");
				out.println("<title>DsSample.java</title>");
				out.println("</head>");
				out.println("<body>");

				//DBへの接続
				InitialContext ic = new InitialContext();
				DataSource ds = (DataSource)ic.lookup(
					"java:/comp/env/jdbc/test" );
				Connection cn = ds.getConnection();

				//SQLの実行
				PreparedStatement st = cn.prepareStatement(
					"select * from SHOHIN" );
				ResultSet rs = st.executeQuery();

				//結果の出力
				while ( rs.next() )	{
					out.println(rs.getInt("NUMBER"));
					out.println(", ");
					out.println(rs.getString("PRODUCT"));
					out.println(", ");
					out.println(rs.getInt("PRICE"));
					out.println("<br>");
				}

				//オブジェクトのclose
				rs.close();
				st.close();
				cn.close();
			}
			catch ( Exception e ) {
				out.println("エラーが発生しました<br>");
				e.printStackTrace( out );
			}
			finally	{
				out.println("</body>");
				out.println("</html>");
			}
		}
}