Detect Session Timeout in Ajax Request in Spring MVC Detect Session Timeout in Ajax Request in Spring MVC ajax ajax

Detect Session Timeout in Ajax Request in Spring MVC


The simplest way for doing this is using a filter on URLs of your AJAX requests.

In the example below I'm just sending HTTP 500 response code with a response body indicating the session timeout, but you can easily set the response code and body to what is more suitable for your case..

package com.myapp.security.authentication;import org.springframework.web.filter.GenericFilterBean;import javax.servlet.FilterChain;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;public class ExpiredSessionFilter extends GenericFilterBean {    static final String FILTER_APPLIED = "__spring_security_expired_session_filter_applied";    @Override    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {        HttpServletRequest request = (HttpServletRequest) req;        HttpServletResponse response = (HttpServletResponse) res;        if (request.getAttribute(FILTER_APPLIED) != null) {            chain.doFilter(request, response);            return;        }        request.setAttribute(FILTER_APPLIED, Boolean.TRUE);        if (request.getRequestedSessionId() != null && !request.isRequestedSessionIdValid()) {                           response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "SESSION_TIMED_OUT");            return;        }        chain.doFilter(request, response);    }}


Here's an approach that I think is quite simple. It's a combination of approaches that I've observed on this site. I wrote a blog post about it:http://yoyar.com/blog/2012/06/dealing-with-the-spring-security-ajax-session-timeout-problem/

The basic idea is to use an api url prefix (i.e. /api/secured) as suggested above along with an authentication entry point. It's simple and works.

Here's the authentication entry point:

package com.yoyar.yaya.config;import org.springframework.security.core.AuthenticationException;import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;import javax.servlet.ServletException;import javax.servlet.http.*;import java.io.IOException;public class AjaxAwareAuthenticationEntryPoint              extends LoginUrlAuthenticationEntryPoint {    public AjaxAwareAuthenticationEntryPoint(String loginUrl) {        super(loginUrl);    }    @Override    public void commence(        HttpServletRequest request,         HttpServletResponse response,         AuthenticationException authException)             throws IOException, ServletException {        boolean isAjax             = request.getRequestURI().startsWith("/api/secured");        if (isAjax) {            response.sendError(403, "Forbidden");        } else {            super.commence(request, response, authException);        }    }}

And here's what goes in your spring context xml:

<bean id="authenticationEntryPoint"  class="com.yoyar.yaya.config.AjaxAwareAuthenticationEntryPoint">    <constructor-arg name="loginUrl" value="/login"/></bean><security:http auto-config="true"  use-expressions="true"  entry-point-ref="authenticationEntryPoint">    <security:intercept-url pattern="/api/secured/**" access="hasRole('ROLE_USER')"/>    <security:intercept-url pattern="/login" access="permitAll"/>    <security:intercept-url pattern="/logout" access="permitAll"/>    <security:intercept-url pattern="/denied" access="hasRole('ROLE_USER')"/>    <security:intercept-url pattern="/" access="permitAll"/>    <security:form-login login-page="/login"                         authentication-failure-url="/loginfailed"                         default-target-url="/login/success"/>    <security:access-denied-handler error-page="/denied"/>    <security:logout invalidate-session="true"                     logout-success-url="/logout/success"                     logout-url="/logout"/></security:http>


I use the same solution by @Matt in backend. If you're using angularJs on front end, add below interceptor in angular $http to let browser actually do a redirect to login page.

var HttpInterceptorModule = angular.module('httpInterceptor', []).config(function ($httpProvider) {  $httpProvider.interceptors.push('myInterceptor');  $httpProvider.defaults.headers.common["X-Requested-With"] = 'XMLHttpRequest'; }) .factory('myInterceptor', function ($q) {return {    'responseError': function(rejection) {      // do something on error        if(rejection.status == 403 || rejection.status == 401) window.location = "login";           return $q.reject(rejection);    }  };

});

Note that below line is needed only if you're using AngularJs after version 1.1.1 (angularJS removed header "X-Requested-With" from that version onward)

$httpProvider.defaults.headers.common["X-Requested-With"] = 'XMLHttpRequest';