CLOVER🍀

That was when it all began.

HttpServletRequest#changeSessionIdを試してみる

Servlet 3.1より入ったという、HttpServletRequest#changeSessionId、これを使うと現在のHttpSessionのIDを変更できるそうな。

あまり使っているサンプルを見たことがなかったので、自分で試してみました。

使用したアプリケーションサーバは、WildFly 8.1.0.Finalとなります。

こんなServletを作成。
src/main/java/org/littlewings/javaee7/servlet/SessionCountingServlet.java

package org.littlewings.javaee7.servlet;

import java.io.IOException;
import java.io.PrintWriter;

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

@WebServlet("/counting")
public class SessionCountingServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse res)
    throws IOException, ServletException {
        res.setCharacterEncoding("UTF-8");

        HttpSession session = req.getSession();
        String firstSessionId = session.getId();

        Integer counter = (Integer)session.getAttribute("counter");
        if (counter == null) {
            counter = 0;
        } else {
            counter++;
        }

        session.setAttribute("counter", counter);

        Cookie sessionCookie = null;
        String sessionCookieName = getServletContext().getSessionCookieConfig().getName();
        for (Cookie c : req.getCookies()) {
            if (sessionCookieName.equals(c.getName())) {
                sessionCookie = c;
            }
        }

        // HttpSessionが存在しない場合に呼び出すと、IllegalStateExceptionがスローされる
        // Undertowの場合、java.lang.IllegalStateException: UT010033: No session
        String changedSessionId = req.changeSessionId();

        PrintWriter writer = res.getWriter();
        String sep = System.lineSeparator();
        String requestedSessionId;
        if (sessionCookie == null) {
            requestedSessionId = "<None>";
        } else {
            requestedSessionId = sessionCookie.getValue();
        }

        writer.write("<!DOCTYPE html>" + sep);
        writer.write("<html>" + sep);
        writer.write("<head>" + sep);
        writer.write("  <meta charset=\"UTF-8\">" + sep);
        writer.write("  <title>サンプル</title>" + sep);
        writer.write("</head>" + sep);
        writer.write("<body>" + sep);
        writer.write("  <h1>HttpServletRequest#changeSessionIdのサンプル</h1>" + sep);
        writer.write("  <div>" + sep);
        writer.write("    <h2>Counter = " + counter + "</h2>" + sep);
        writer.write("    <p>RequestedSessionId = " + requestedSessionId + "</p>" + sep);
        writer.write("    <p>FirstSessionId = " + firstSessionId + "</p>" + sep);
        writer.write("    <p>changedSessionId = " + changedSessionId + "</p>" + sep);
        writer.write("    <p>currentSessionId = " + session.getId() + "</p>" + sep);
        writer.write("  </div>" + sep);
        writer.write("</body>" + sep);
        writer.write("</html>" + sep);
    }
}

使い方は簡単、HttpServletRequest#changeSessionIdを呼び出すと、HttpSessionのIDを変更すると共に、新しいSession IDが返却されます。

        String changedSessionId = req.changeSessionId();

なお、それ以降に呼び出すHttpSession#getIdも、HttpServletRequest#changeSessionIdの戻り値と同じ値を戻すようになります。

session.getId();

なお、HttpServletRequest#changeSessionIdを呼び出した際にまだHttpSessionが存在しない状態だと、IllegalStateExceptionがスローされます。

        // HttpSessionが存在しない場合に呼び出すと、IllegalStateExceptionがスローされる
        // Undertowの場合、java.lang.IllegalStateException: UT010033: No session
        String changedSessionId = req.changeSessionId();

今回のサンプルでは、SessionのIDを変更してもSessionに登録した値が参照できることを確認しつつ、リクエストされたCookieに乗ってくる値と変更前のSession IDの値、HttpServletRequest#changeSessionIdの戻り値とHttpServletRequest#changeSessionId呼び出し後のHttpSession#getIdの呼び出し結果を並べてみました。

        writer.write("    <h2>Counter = " + counter + "</h2>" + sep);
        writer.write("    <p>RequestedSessionId = " + requestedSessionId + "</p>" + sep);
        writer.write("    <p>FirstSessionId = " + firstSessionId + "</p>" + sep);
        writer.write("    <p>changedSessionId = " + changedSessionId + "</p>" + sep);
        writer.write("    <p>currentSessionId = " + session.getId() + "</p>" + sep);

では、WildFlyにデプロイして動作確認。

初回アクセス。以下は、Firefoxで取ったキャプチャです。

なんか、初回なのにCookieが来たことになってる…。

2回目。

3回目。

とりあえず、Session IDが変更できること、Session IDを変更してもあらかじめsetAttributeした値は継続されることを確認できました。

が、HttpServletRequest#getCookiesで、リクエストされていないSession用のCookieが取得できたのが気になりますね…。そういうもんでしたっけ?

ちょっと確認。こういうデバッグコード仕込んで

        System.out.println("==========================================");
        System.out.println("session new? = " + session.isNew());
        System.out.println("Session Cookie Name = " + sessionCookieName);

        java.util.Enumeration<String> headerNames = req.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String header = headerNames.nextElement();
            System.out.println("Header: " + header + " = " + req.getHeader(header));
        }

        for (Cookie c : req.getCookies()) {
            System.out.println(c);
            if (sessionCookieName.equals(c.getName())) {
                sessionCookie = c;
            }
        }
        System.out.println("==========================================");

デプロイしてCookie消してアクセス。

初回。

18:36:20,995 INFO  [stdout] (default task-13) ==========================================
18:36:21,010 INFO  [stdout] (default task-13) session new? = true
18:36:21,010 INFO  [stdout] (default task-13) Session Cookie Name = JSESSIONID
18:36:21,011 INFO  [stdout] (default task-13) Header: Accept = text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
18:36:21,012 INFO  [stdout] (default task-13) Header: Cache-Control = max-age=0
18:36:21,023 INFO  [stdout] (default task-13) Header: Connection = keep-alive
18:36:21,024 INFO  [stdout] (default task-13) Header: User-Agent = Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:32.0) Gecko/20100101 Firefox/32.0
18:36:21,024 INFO  [stdout] (default task-13) Header: Host = localhost:8080
18:36:21,024 INFO  [stdout] (default task-13) Header: Accept-Language = ja,en-us;q=0.7,en;q=0.3
18:36:21,025 INFO  [stdout] (default task-13) Header: Accept-Encoding = gzip, deflate
18:36:21,025 INFO  [stdout] (default task-13) Cookie: JSESSIONID = THOeQGeYV1r3ltwT7kdO54zz.localhost
18:36:21,026 INFO  [stdout] (default task-13) ==========================================

やっぱりCookieヘッダないですよね。

2回目。

18:37:01,786 INFO  [stdout] (default task-14) ==========================================
18:37:01,789 INFO  [stdout] (default task-14) session new? = false
18:37:01,789 INFO  [stdout] (default task-14) Session Cookie Name = JSESSIONID
18:37:01,790 INFO  [stdout] (default task-14) Header: Cookie = JSESSIONID=5TvyeEkjegTF8XWsS2eYlGYg.localhost
18:37:01,790 INFO  [stdout] (default task-14) Header: Accept = text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
18:37:01,790 INFO  [stdout] (default task-14) Header: Cache-Control = max-age=0
18:37:01,790 INFO  [stdout] (default task-14) Header: Connection = keep-alive
18:37:01,790 INFO  [stdout] (default task-14) Header: User-Agent = Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:32.0) Gecko/20100101 Firefox/32.0
18:37:01,790 INFO  [stdout] (default task-14) Header: Host = localhost:8080
18:37:01,790 INFO  [stdout] (default task-14) Header: Accept-Language = ja,en-us;q=0.7,en;q=0.3
18:37:01,790 INFO  [stdout] (default task-14) Header: Accept-Encoding = gzip, deflate
18:37:01,791 INFO  [stdout] (default task-14) Cookie: JSESSIONID = 5TvyeEkjegTF8XWsS2eYlGYg.localhost
18:37:01,791 INFO  [stdout] (default task-14) ==========================================

ここで初めて、Cookieヘッダが現れますね。

18:37:01,790 INFO  [stdout] (default task-14) Header: Cookie = JSESSIONID=5TvyeEkjegTF8XWsS2eYlGYg.localhost

ということは、やっぱり送信されていないCookieが取れていることに…。

Tomcatだと、こういう挙動じゃなかった気がしますけど、Tomcat 8とかで確認すると違うのかな…。