Dealing with the Spring Security Ajax Session Timeout Problem

{10 Comments}

I’m using Spring MVC with Spring Security for my project and I rigged up a nice set of tabs with jquery-ui. Each tab loads using ajax. It’s slick. Until the session times out. Then you see the login page inside of the tab. Ugly!

First of all I recommend putting your ajax api behind a url prefix that indicates that the api is protected. This is not only clear, but it allows you to write the following authentication entry point without having to check for X-Ajax headers and the like. It also alleviates the need to send special headers with each ajax request which I surely would screw up and forget at some point.

The entry point as you can see is just a regular java POJO.

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);
        }
    }
}

In this case I’m extending LoginUrlAuthenticationEntryPoint which will make sense for most applications. But if you are doing something special you might need to extend another entry point base class. It depends on your specific situation.

Spring entry points are called when an unauthenticated user tries to access a secured page. In the class above for any pages outside of the api the normal course of events takes place. For the calls under /api/secured the ajax handling kicks in.

To enable your custom entry point your context xml will look something like the following:

    <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>

Key things to notice:

  • The entry-point-ref="authenticationEntryPoint" attribute is set to the bean id of the entry point implementation.
  • The entry point requires constructor injection of the login url.
  • On the client side it is a simple matter of checking for the 403 http error code and redirecting the page appropriately.
  • /api/secured/** is secured so the entry point will be called when an unauthenticated user tries to access the api.

10 Comments…

 Share your views
  1. I tried other methods and failed. This one worked like a charm, thanks for posting!

  2. If you are using request cache and authentication fails on an AJAX url, your browser will redirect to that AJAX url after the user logs in. A better approach is to extend ExceptionTranslationFilter and override sendStartAuthentication method.

  3. It worked for me too :)

  4. It worked for me too. Thanks. Right now I get a 403 as ajax response, is there a way the server can redirect to login page instead of sending this response ?

  5. I tried using this but the only time it hits the AjaxAwareAuthenticationEntryPoint class is when the user is first accessing the website. I am extending LoginUrlAuthenticationEntryPoint. Any ideas what I may be doing wrong?

Leave a Comment

Your email address will not be published.