Spring Boot + Spring Security使用時のSessionTimeout対応

2016/06/25

Spring BootにSpring Securityを入れた時のSessionTimeoutのデフォルト挙動は、ログイン画面への自動遷移になる。
一般的な要件として、ログイン画面に遷移したときに「タイムアウトしました。」などのメッセージを表示しなければいけないような時の対応方法を記載する。
※関連ページ:Spring Boot + Spring Security使用時のCSRFとSessionTimeoutの問題

検証version
・Spring Boot 1.3.3
・Spring Security 4.0.3
※参考までにView側技術
 ・Thymeleaf 2.1.4
 ・Bootstrap 3.3.6

org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPointというExceptionが発生したときのログイン画面へのリダイレクト用クラスをSpring Securityが用意していて、これを拡張してすることで実現する。
LoginUrlAuthenticationEntryPoint

Used by the ExceptionTranslationFilter to commence a form login authentication via the UsernamePasswordAuthenticationFilter.
Holds the location of the login form in the loginFormUrl property, and uses that to build a redirect URL to the login page.

拡張方法は、リダイレクトURLを決定するメソッドbuildRedirectUrlToLoginPageをOverrideする。

処理の内容は、Sessionが無効状態になっているとき、デフォルトのリダイレクトURLの後ろにリクエストパラメータ"timeout"を付与する。
これにより、/login?timeoutというURLになる。

しかしAjaxリクエストの場合はリダイレクトが動かないため、リクエストがAjaxかどうかを判定して、Ajaxの場合はHTTP STATUS:401 Unauthorizedを返すのみにして、JavaScript側でリダイレクトするように対応する必要がある。
まずは、リクエストがAjaxかどうかを判定するために、LoginUrlAuthenticationEntryPoint#commenceをOverrideする。

JavaScript側はjQueryを用いて書くと以下のようになる。

login.htmlでは、 th:if="${param.timeout}" という条件式を設定して、リクエストパラメータにtimeoutが存在するときのみ「タイムアウトしました。」という文言を表示するようにしている。
以下はlogin.htmlのBODY部。

あとは、拡張したクラスをSpring Securityに認識させることで、SessionTimeout時のリダイレクトとリダイレクト先での文言表示が実現できる。

まず、拡張クラスSessionExpiredDetectingLoginUrlAuthenticationEntryPointの全文を以下に掲載する。

SessionExpiredDetectingLoginUrlAuthenticationEntryPointをSpring SecurityのJavaConfigで設定する。

WebSecurityConfigクラスでのポイントは2点。

  • exceptionHandling().authenticationEntryPoint()に拡張したSessionExpiredDetectingLoginUrlAuthenticationEntryPointを設定する
  • /login?timeoutを認証不要のURLと認識させるため、authorizeRequests().antMatchers("/login", "/login?**").permitAll()を設定する。
    よくあるSpring Securityのサンプルでは、 以下のようになっていることが多いが、authorizeRequests().antMatchers("/login", "/login?**").permitAll()を設定するため、formLogin()とlogout()の箇所でpermitAll()をせず、authorizeRequests()でpermitAll()する。

以上でSpring Boot + Spring Security使用時のSessionTimeout対応は完了。
ただし、CSRF対策が有効の場合、POST時にSessionTimeoutしているとHTTP Status:403 Forbiddenが発生してしまう問題がある。
CSRFとSessionTimeout問題は別ページで対応策を記載する。
Spring Boot + Spring Security使用時のCSRFとSessionTimeoutの問題

-Java
-