{"id":950,"date":"2012-04-21T14:49:18","date_gmt":"2012-04-21T12:49:18","guid":{"rendered":"http:\/\/doanduyhai.wordpress.com\/?p=950"},"modified":"2012-04-21T14:49:18","modified_gmt":"2012-04-21T12:49:18","slug":"spring-security-part-vi-session-timeout-handling-for-ajax-calls","status":"publish","type":"post","link":"https:\/\/www.doanduyhai.com\/blog\/?p=950","title":{"rendered":"Spring Security part VI : Session Timeout handling for Ajax calls"},"content":{"rendered":"<p>Recently when developing the <a title=\"Tatami doanduyhai fork\" href=\"https:\/\/github.com\/doanduyhai\/tatami\/network\" target=\"_blank\">Tatami<\/a> application for the Twitter-like contest, I faced an annoying issue: <strong>how to detect an user session timeout when an Ajax request is triggered from the browser ?<\/strong><\/p>\n<p>If you&#8217;re not familiar yet with Spring Security, you can check my previous articles on this framework.<\/p>\n<p>In this article we&#8217;ll see a solution based on <strong>Spring Security<\/strong> filter. For those who don&#8217;t use Spring Security I&#8217;ll show another approach with servlet filter.<\/p>\n<p><!--more--><\/p>\n<blockquote><p>Please note that a demo application for this article can be found on GitHub <a href=\"https:\/\/github.com\/doanduyhai\/AjaxSessionExpiration\" title=\"https:\/\/github.com\/doanduyhai\/AjaxSessionExpiration\" target=\"_blank\">https:\/\/github.com\/doanduyhai\/AjaxSessionExpiration<\/a><\/p><\/blockquote>\n<p> <em>Edit: the implementation has been changed to simplify Ajax request detection thanks to Marty Jones suggestions.<\/em><br \/>\n&nbsp;<\/p>\n<h1>I The problem<\/h1>\n<p>Nowadays mobile development is getting more and more important for the business. Many companies provide a mobile interface of their traditional desktop website to address a larger audience.<\/p>\n<p>The corner-stone of mobile architecture are RESTfull webservices. The underlying implementation relies on Ajax calls to achieve content lazy loading.<\/p>\n<p>If your resources is protected by a security framework any un-authenticated request will be rejected and the framework redirects you to a login page. The same mechanism applies if the request is Ajax-based.<\/p>\n<p>However, at the <strong>XMLHttpRequest<\/strong> level, it is not possible to detect this redirection. According to the <a title=\"Redirection 3xx\" href=\"http:\/\/www.w3.org\/Protocols\/rfc2616\/rfc2616-sec10.html#sec10.3\" target=\"_blank\">W3C specs<\/a>, the redirection is taken care at browser level and should be transparent for the user (so transparent for the <strong>XMLHttpRequest<\/strong> protocol too). What happens it that the Ajax layer receives an <strong>HTTP 200 code<\/strong> after the redirection.<\/p>\n<p>This issue has been widely debated on Stackoverflow at this <a title=\"http:\/\/stackoverflow.com\/questions\/199099\/how-to-manage-a-redirect-request-after-a-jquery-ajax-call\" href=\"http:\/\/stackoverflow.com\/questions\/199099\/how-to-manage-a-redirect-request-after-a-jquery-ajax-call\" target=\"_blank\">thead<\/a>.<br \/>\nMany contributors suggest to add a special attribute in the JSON response to indicates a redirect or to add a special Javascript callback to parse the response string searching for a DOM element that characterizes the login page and so on.<\/p>\n<p>My opinion is that all these solutions are not satisfactory because they require many hacks and tamper with the response string\/content itself.<br \/>\n&nbsp;<\/p>\n<h1>II The solution<\/h1>\n<h3>A Spring Security<\/h3>\n<h5><strong>1) Algorithm<\/strong><\/h5>\n<p>Since it is clear that it&#8217;s not possible to detect an HTTP Redirect at Javascript level, the job should be done at server side.<\/p>\n<p>The idea is to add a special filter in the Spring Security chain to detect a session timeout and an incoming Ajax call then return an custom HTTP error code so the Ajax callback function can detect and process properly.<\/p>\n<p>Below is a pseudo-code of the filter implementation:<\/p>\n<ul>\n<li>If not authenticated\n<ul>\n<li>Redirect to login page<\/li>\n<\/ul>\n<\/li>\n<li>If the access to the resource is denied\n<ul>\n<li>If the session has expired and the request is Ajax-based\n<ul>\n<li>Return custom HTTP error code<\/li>\n<\/ul>\n<\/li>\n<li>Else\n<ul>\n<li>Redirect to login page<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<li>Else\n<ul>\n<li>Redirect to login page<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h5><strong>2) Implementation<\/strong><\/h5>\n<p> We should create a custom filter class implementing the <strong>org.springframework.web.filter.GenericFilterBean<\/strong> interface.<\/p>\n<pre class=\"brush: java; highlight: [9,46,48]; title: ; wrap-lines: false; notranslate\" title=\"\">\npublic class AjaxTimeoutRedirectFilter extends GenericFilterBean\n{\n\n\tprivate static final Logger logger = LoggerFactory.getLogger(AjaxTimeoutRedirectFilter.class);\n\n\tprivate ThrowableAnalyzer throwableAnalyzer = new DefaultThrowableAnalyzer();\n\tprivate AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();\n\n\tprivate int customSessionExpiredErrorCode = 901;\n\n\t@Override\n\tpublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException\n\t{\n\t\ttry\n\t\t{\n\t\t\tchain.doFilter(request, response);\n\n\t\t\tlogger.debug(&amp;quot;Chain processed normally&amp;quot;);\n\t\t}\n\t\tcatch (IOException ex)\n\t\t{\n\t\t\tthrow ex;\n\t\t}\n\t\tcatch (Exception ex)\n\t\t{\n\t\t\tThrowable[] causeChain = throwableAnalyzer.determineCauseChain(ex);\n\t\t\tRuntimeException ase = (AuthenticationException) throwableAnalyzer.getFirstThrowableOfType(AuthenticationException.class, causeChain);\n\n\t\t\tif (ase == null)\n\t\t\t{\n\t\t\t\tase = (AccessDeniedException) throwableAnalyzer.getFirstThrowableOfType(AccessDeniedException.class, causeChain);\n\t\t\t}\n\n\t\t\tif (ase != null)\n\t\t\t{\n\t\t\t\tif (ase instanceof AuthenticationException)\n\t\t\t\t{\n\t\t\t\t\tthrow ase;\n\t\t\t\t}\n\t\t\t\telse if (ase instanceof AccessDeniedException)\n\t\t\t\t{\n\n\t\t\t\t\tif (authenticationTrustResolver.isAnonymous(SecurityContextHolder.getContext().getAuthentication()))\n\t\t\t\t\t{\n\t\t\t\t\t\tlogger.info(&amp;quot;User session expired or not logged in yet&amp;quot;);\n\t\t\t\t\t\tString ajaxHeader = ((HttpServletRequest) request).getHeader(&amp;quot;X-Requested-With&amp;quot;);\n\n\t\t\t\t\t\tif (&amp;quot;XMLHttpRequest&amp;quot;.equals(ajaxHeader))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tlogger.info(&amp;quot;Ajax call detected, send {} error code&amp;quot;, this.customSessionExpiredErrorCode);\n\t\t\t\t\t\t\tHttpServletResponse resp = (HttpServletResponse) response;\n\t\t\t\t\t\t\tresp.sendError(this.customSessionExpiredErrorCode);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tlogger.info(&amp;quot;Redirect to login page&amp;quot;);\n\t\t\t\t\t\t\tthrow ase;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tthrow ase;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\t}\n\n\tprivate static final class DefaultThrowableAnalyzer extends ThrowableAnalyzer\n\t{\n\t\t\/**\n\t\t * @see org.springframework.security.web.util.ThrowableAnalyzer#initExtractorMap()\n\t\t *\/\n\t\tprotected void initExtractorMap()\n\t\t{\n\t\t\tsuper.initExtractorMap();\n\n\t\t\tregisterExtractor(ServletException.class, new ThrowableCauseExtractor()\n\t\t\t{\n\t\t\t\tpublic Throwable extractCause(Throwable throwable)\n\t\t\t\t{\n\t\t\t\t\tThrowableAnalyzer.verifyThrowableHierarchy(throwable, ServletException.class);\n\t\t\t\t\treturn ((ServletException) throwable).getRootCause();\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t}\n\n\tpublic void setCustomSessionExpiredErrorCode(int customSessionExpiredErrorCode)\n\t{\n\t\tthis.customSessionExpiredErrorCode = customSessionExpiredErrorCode;\n\t}\n}\n<\/pre>\n<p> This class is a copy of the <strong>org.springframework.security.web.access.ExceptionTranslationFilter<\/strong> class with some modifications to detect session timeout with Ajax-based request.<\/p>\n<p> First we define 1 new attribute:<\/p>\n<ul>\n<li><strong>customSessionExpiredErrorCode<\/strong>: custom HTTP error code to return for session timeout with Ajax-based request. The default value is <strong>901<\/strong><\/li>\n<\/ul>\n<p>The main job is done at <strong>lines 46 &amp; 48<\/strong>. We compare the request header &#8220;<strong>X-Requested-With<\/strong>&#8220;, when it exists, with the value &#8220;<strong>XMLHttpRequest<\/strong>&#8221; to detect whether the current request is Ajax-based or not. Please note that I did the test with IE 9 only, not sure it works for older IE version. <\/p>\n<p> In most cases we simply re-throw the exception except when the call is Ajax-based. In this case we force an HTTP error code and completely <strong>bypass the remaining filters in the security filter chain<\/strong>.<\/p>\n<p>&nbsp;<\/p>\n<h5><strong>3) Filter configuration<\/strong><\/h5>\n<p>The idea is to add the above custom filter in the Spring Security filter chain. The order in the filter chain is crucial. Our filter should intercept the session timeout for Ajax calls <strong>before the vanilla ExceptionTranslationFilter<\/strong> in order to send the custom HTTP error code. <\/p>\n<p>Configuration for Spring Security namespace users:<\/p>\n<pre class=\"brush: xml; highlight: [2,10]; title: ; wrap-lines: false; notranslate\" title=\"\">\n...\n&amp;lt;beans:bean id=&amp;quot;ajaxTimeoutRedirectFilter&amp;quot; class=&amp;quot;doan.ajaxsessionexpiration.demo.security.AjaxTimeoutRedirectFilter&amp;quot;&amp;gt;\n\t&amp;lt;beans:property name=&amp;quot;customSessionExpiredErrorCode&amp;quot; value=&amp;quot;901&amp;quot;\/&amp;gt;\n&amp;lt;\/beans:bean&amp;gt;\n...\n &amp;lt;http auto-config=&amp;quot;true&amp;quot; use-expressions=&amp;quot;true&amp;quot;&amp;gt;\n        &amp;lt;intercept-url pattern=&amp;quot;\/**&amp;quot; access=&amp;quot;isAuthenticated()&amp;quot; \/&amp;gt;\n        &amp;lt;custom-filter ref=&amp;quot;ajaxTimeoutRedirectFilter&amp;quot; after=&amp;quot;EXCEPTION_TRANSLATION_FILTER&amp;quot;\/&amp;gt;\n\t...\n\t...\n&amp;lt;\/http&amp;gt;\n<\/pre>\n<p> Please notice that at <strong>line 10<\/strong> we define our custom filter which is placed <strong>AFTER the EXCEPTION_TRANSLATION_FILTER in the chain<\/strong>. Being put later in the chain means that our filter can catch security exceptions <strong>before the EXCEPTION_TRANSLATION_FILTER<\/strong> which is what we want.<\/p>\n<p>Configuration without Spring Security namespace:<\/p>\n<pre class=\"brush: xml; highlight: [2,16]; title: ; wrap-lines: false; notranslate\" title=\"\">\n...\n&amp;lt;bean id=&amp;quot;ajaxTimeoutRedirectFilter&amp;quot; class=&amp;quot;doan.ajaxsessionexpiration.demo.security.AjaxTimeoutRedirectFilter&amp;quot;&amp;gt;\n\t&amp;lt;property name=&amp;quot;customSessionExpiredErrorCode&amp;quot; value=&amp;quot;901&amp;quot;\/&amp;gt;\n\t&amp;lt;property name=&amp;quot;restString&amp;quot; value=&amp;quot;\/rest&amp;quot;\/&amp;gt;\n\t&amp;lt;property name=&amp;quot;restStringPatternMode&amp;quot; value=&amp;quot;PREFIX&amp;quot;\/&amp;gt;\n&amp;lt;\/bean&amp;gt;\n...\n&amp;lt;bean id=&amp;quot;securityFilterChain&amp;quot; class=&amp;quot;org.springframework.security.web.FilterChainProxy&amp;quot;&amp;gt;\n\t&amp;lt;sec:filter-chain-map request-matcher=&amp;quot;ant&amp;quot;&amp;gt;\n\t    &amp;lt;sec:filter-chain pattern=&amp;quot;\/**&amp;quot;  filters=&amp;quot;\n\t           securityContextPersistentFilter,\n\t           logoutFilter,\n\t           authenticationProcessingFilter,\n\t           anonymousFilter,\n\t           exceptionTranslationFilter,\n\t           ajaxTimeoutRedirectFilter,\n\t           filterSecurityInterceptor\n\t           &amp;quot; \/&amp;gt;\n  \t&amp;lt;\/sec:filter-chain-map&amp;gt;\n&amp;lt;\/bean&amp;gt;\n<\/pre>\n<h3>B Other security frameworks<\/h3>\n<p> If you do not use Spring Security, there is a generic solution though. The idea is to add a special Http filter and check for HTTP session validity. If the session is no longer valid (timeout) and if the request is Ajax-base, you send a custom HTTP error as above.<\/p>\n<p> First you should add a custom filter in the web.xml file:<\/p>\n<pre class=\"brush: xml; highlight: [3,4,20,21]; title: ; wrap-lines: false; notranslate\" title=\"\">\n...\n&amp;lt;filter&amp;gt;\n\t&amp;lt;filter-name&amp;gt;ajaxSessionExpirationFilter&amp;lt;\/filter-name&amp;gt;\n\t&amp;lt;filter-class&amp;gt;doan.ajaxsessionexpiration.demo.security.AjaxSessionExpirationFilter&amp;lt;\/filter-class&amp;gt;\n\t&amp;lt;init-param&amp;gt;\n\t\t&amp;lt;param-name&amp;gt;customSessionExpiredErrorCode&amp;lt;\/param-name&amp;gt;\n\t\t&amp;lt;param-value&amp;gt;901&amp;lt;\/param-value&amp;gt;\n\t&amp;lt;\/init-param&amp;gt;\n&amp;lt;\/filter&amp;gt;\n...\n&amp;lt;filter-mapping&amp;gt;\n    \t&amp;lt;filter-name&amp;gt;ajaxSessionExpirationFilter&amp;lt;\/filter-name&amp;gt;\n    \t&amp;lt;url-pattern&amp;gt;\/*&amp;lt;\/url-pattern&amp;gt;\n&amp;lt;\/filter-mapping&amp;gt;\n<\/pre>\n<p> Please note that this filter should be the one to kick in first (the first in the filter chain).<\/p>\n<p> Then the implementation:<\/p>\n<pre class=\"brush: java; highlight: [18,19]; title: ; wrap-lines: false; notranslate\" title=\"\">\npublic class AjaxSessionExpirationFilter implements Filter\n{\n\n\tprivate int customSessionExpiredErrorCode = 901;\n\n    \t@Override\n\tpublic void init(FilterConfig arg0) throws ServletException\n\t{\n\t\t\/\/ Property check here\n\t}\n\n    \t@Override\n\tpublic void doFilter(ServletRequest request, ServletResponse response, FilterChain filerChain) throws IOException, ServletException\n\t{\n\t\tHttpSession currentSession = ((HttpServletRequest)request).getSession(false);\n\t\tif(currentSession == null)\n\t\t{\n\t\t\tString ajaxHeader = ((HttpServletRequest) request).getHeader(&amp;quot;X-Requested-With&amp;quot;);\n\t\t\tif(&amp;quot;XMLHttpRequest&amp;quot;.equals(ajaxHeader))\n\t\t\t{\n\t\t\t\tlogger.info(&amp;quot;Ajax call detected, send {} error code&amp;quot;, this.customSessionExpiredErrorCode);\n\t\t\t\tHttpServletResponse resp = (HttpServletResponse) response;\n\t\t\t\tresp.sendError(this.customSessionExpiredErrorCode);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t\/\/ Redirect to login page\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t\/\/ Redirect to login page\n\t\t}\n\t}\n}\n<\/pre>\n<p> The session timeout detection is done at <strong>line 21<\/strong>. The <em>getSession(<strong>false<\/strong>)<\/em> method of the <strong>HttpServletRequest<\/strong> interface will return a HTTP session if one exists (and has not expired yet) but will not create a new one if no session exists or if the current session has expired.<\/p>\n<p> The rest of the processing is similar to the Spring Security version.<br \/>\n&nbsp;<\/p>\n<h3>C Client-side implementation<\/h3>\n<p> On the client side we should configure the Ajax callback function so it can handle properly our custom HTTP error code. For this jQuery comes to the rescue:<\/p>\n<pre class=\"brush: xml; highlight: [9,10,12]; title: ; wrap-lines: false; notranslate\" title=\"\">\n&amp;lt;script&amp;gt; \nfunction ajaxSessionTimeout()\n{\n\t\/\/ Handle Ajax session timeout here\n}\n\n!function( $ )\n{\n\t$.ajaxSetup({\n\t\tstatusCode: \n\t\t{\n\t\t\t901: ajaxSessionTimeout\n\t\t}\n\t});\n}(window.jQuery);\n&amp;lt;\/script&amp;gt;\n<\/pre>\n<p> We use the <strong>jQuery<\/strong>.<em>ajaxSetup()<\/em> function to define a custom behavior when an HTTP 901 status code is encountered. This is a mere callback to a function to handle Ajax request timout.<\/p>\n<p> And that&#8217;s all !<br \/>\n&nbsp;<\/p>\n<h1>III Demo application<\/h1>\n<p>I&#8217;ve created a demo application to illustrate this article. I set the session timeout to <strong>1 minute<\/strong>.<\/p>\n<p>On the main page, we have a popover element which trigger Ajax call to fetch user name from server.<\/p>\n<p><a href=\"https:\/\/www.doanduyhai.com\/blog\/wp-content\/uploads\/2012\/04\/ajaxsessionexpiration-popover.png\"><img loading=\"lazy\" src=\"https:\/\/www.doanduyhai.com\/blog\/wp-content\/uploads\/2012\/04\/ajaxsessionexpiration-popover.png\" alt=\"ajaxsessionexpiration-popover\" title=\"ajaxsessionexpiration-popover\" width=\"630\" height=\"241\" class=\"aligncenter size-full wp-image-967\" srcset=\"https:\/\/www.doanduyhai.com\/blog\/wp-content\/uploads\/2012\/04\/ajaxsessionexpiration-popover.png 705w, https:\/\/www.doanduyhai.com\/blog\/wp-content\/uploads\/2012\/04\/ajaxsessionexpiration-popover-300x115.png 300w\" sizes=\"(max-width: 630px) 100vw, 630px\" \/><\/a><\/p>\n<p> After 1 minute the session expires, the Ajax error callback for Ajax session timeout is called. In this case I display a modal panel informing the user that his session has expired and prompting him to go back to login page to re-authenticate.<\/p>\n<p> <a href=\"https:\/\/www.doanduyhai.com\/blog\/wp-content\/uploads\/2012\/04\/ajaxsessionexpiration-session-timout-modal.png\"><img loading=\"lazy\" src=\"https:\/\/www.doanduyhai.com\/blog\/wp-content\/uploads\/2012\/04\/ajaxsessionexpiration-session-timout-modal.png\" alt=\"\" title=\"ajaxsessionexpiration-session-timout-modal\" width=\"556\" height=\"160\" class=\"aligncenter size-full wp-image-969\" srcset=\"https:\/\/www.doanduyhai.com\/blog\/wp-content\/uploads\/2012\/04\/ajaxsessionexpiration-session-timout-modal.png 556w, https:\/\/www.doanduyhai.com\/blog\/wp-content\/uploads\/2012\/04\/ajaxsessionexpiration-session-timout-modal-300x86.png 300w\" sizes=\"(max-width: 556px) 100vw, 556px\" \/><\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Recently when developing the Tatami application for the Twitter-like contest, I faced an annoying issue: how to detect an user session timeout when an Ajax request is triggered from the browser ? If you&#8217;re not familiar yet with Spring Security,&#8230;<br \/><a class=\"read-more-button\" href=\"https:\/\/www.doanduyhai.com\/blog\/?p=950\">Read more<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[30,14,19],"tags":[],"_links":{"self":[{"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/950"}],"collection":[{"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=950"}],"version-history":[{"count":0,"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/950\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=950"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=950"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=950"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}