Servlet APIにおけるHttpSessionの挙動検証
Javaでよくあるセッション管理の仕組みについて他人に説明している時に、どうも自分の認識があいまいになっていることに気付いたので、挙動を検証してみます。よくあるセッション管理ということで、Servlet APIにおけるHttpSessionの挙動を検証します。
検証するWebアプリケーション
アクセスするとcount=1というボディを返し、更にアクセスするとcount=2、count=3とカウントアップするだけのWebアプリケーションです。もちろん、異なるWebブラウザからアクセスした場合はcount=1からやり直しです。Webブラウザごとにセッション管理を行い、カウントを保持します。
検証のために作成したWebアプリケーションは、次のリポジトリにあります。検証では詳細に説明はしないので、READMEやソースコードをご覧ください。
検証
セッション・タイムアウトを何も設定しない場合
セッション・タイムアウトはsrc/main/webapp/WEB-INF/web.xmlの<session-timeout>で設定しますが、まずはweb.xmlを削除してデフォルトの挙動を確認してみます。
Webブラウザを開いてhttp://localhost:8080/にアクセスすると、count=1が表示されます。この時、レスポンスにSet-CookieにJSESSIONIDが設定されます。ExpiresやMax-Ageが未設定なので、このJSESSIONIDはWebブラウザを閉じたときにWebブラウザから削除されます。

次にアクセスすると、count=2が表示されます。この時、リクエストに先ほどのJSESSIONIDが設定されています。このJSESSIONIDにより先ほどと同一セッションと判定されて、サーバー側でカウントアップ処理が行われます。

Webブラウザを閉じてアクセスすると、count=1が表示されます。この時、リクエストにはJSESSIONIDが設定されておらず、レスポンスにSet-Cookieで新しいJSESSIONIDが設定されます。上で説明した通り、Webブラウザを閉じたためにJSESSIONIDがWebブラウザから削除され、このような挙動になります。

なお、Webアプリケーション側はセッションを無期限で保持します。
セッション・タイムアウトを設定した場合
セッション・タイムアウトを1分に設定した時の挙動を確認してみます。
まず、web.xmlを次のように修正します。
<session-config>
<session-timeout>1</session-timeout>
</session-config>
<session-timeout>は、タイムアウト時間を分単位で設定します。
Webブラウザを開いてhttp://localhost:8080/にアクセスすると、先ほどと同様にcount=1が表示されます。レスポンスも先ほどと同様にSet-CookieでJSESSIONIDが設定されており、ExpiresやMax-Ageが未設定なのでWebブラウザを閉じたときに削除されます。

次に1分以内にアクセスすると、count=2が表示されます。同様にアクセスし続けると、カウントアップされ続けます。Webアプリケーション側のセッション・タイムアウト時刻は、アクセスするたびにリセットされます。

次に1分以上後にアクセスすると、count=1が表示されます。リクエストを見るとJSESSIONIDが設定されていますが、レスポンスを見るとSet-Cookieでリクエストとは異なるJSESSIONIDが設定されています。つまり、Webアプリケーション側でセッションが破棄され、新しいセッションが開始されたということです。

なお、1分以内にWebブラウザを閉じて再びアクセスしてもcount=1が表示されます。これは、Webブラウザを閉じたためにJSESSIONIDが削除されたためです。
Cookie寿命を設定した場合
セッション・タイムアウトを1分、Cookie寿命を2分に設定した時の挙動を確認してみます。
まず、web.xmlを次のように修正します。
<session-config>
<session-timeout>1</session-timeout>
<cookie-config>
<max-age>120</max-age>
</cookie-config>
</session-config>
<cookie-config>/<max-age>は、Cookie寿命を秒単位で設定します。これを設定することで、Set-CookieにExpiresとMax-Ageが設定されるようになります。
セッション・タイムアウトは、Webアプリケーションにアクセスするごとにタイムアウト時刻が更新されます。これに対してCookie寿命は、最初にSet-Cookieされたときに設定されそのあとは更新されないために、寿命の時刻が来るとWebブラウザから削除されます。Set-CookieにExpiresとMax-Ageが設定されるためにWebブラウザを閉じても削除されないため、Webブラウザのライフサイクルをまたいでセッションを保持することができます。
実際の挙動を確認してみます。Webブラウザを開いてhttp://localhost:8080/にアクセスすると、これまでと同様にcount=1が表示されます。レスポンスにSet-CookieでJSESSIONIDが設定されますが、先ほどと異なりExpiresとMax-Ageが設定されます。

1分以内に同様にアクセスすると、カウントアップされ続けます。

Webブラウザを更新し続けていると、最初のアクセスから2分経過後にcount=1に戻ります。リクエストを見るとそれまで設定されていたJSESSIONIDが設定されておらず、リクエストにJSESSIONIDがないのでレスポンスのSet-Cookieで新しいJSESSIONIDが設定されます。

なお、1分以内にWebブラウザを閉じて再びアクセスした場合、閉じる前のセッションが保持されているためにカウントアップが継続されます。
おわりに
どのようにタイムアウトするのか、通信がどのように行われるのか認識があいまいでしたが、整理できました。次はSpring SessionやSpring Securityにおけるセッション管理を検証してみたいと思います。