Spring Security bietet in der Standardkonfiguration keine ordentliche Ajax Unterstützung. Bei fehlender oder abgelaufener Session wird standardmäßig auf die Anmeldeseite weitergeleitet. Spring Security kann jedoch so konfiguriert werden, dass entsprechende Anfragen mit einem HTTP Status 401 (Unauthorized) beantwortet werden.

Hierzu ist eine Erweiterung des LoginUrlAuthenticationEntryPoint nötig. Die commence Methode wird von Spring Security zur Behandlung von Anfragen mit fehlender Session aufgerufen. Hier findet standardmäßig nur eine Weiterleitung auf die Anmeldeseite statt. Anhand des X-Requested-With Headers können Ajax Anfragen jedoch erkannt und gesondert behandelt werden:

package de.patrickgotthard.security;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.http.HttpStatus;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;

/**
 * Ajax aware {@link LoginUrlAuthenticationEntryPoint}.
 */
public class AjaxAwareAuthenticationEntryPoint extends LoginUrlAuthenticationEntryPoint {

    public AjaxAwareAuthenticationEntryPoint(final String loginFormUrl) {
        super(loginFormUrl);
    }

    /**
     * Sends HTTP status 401 (Unauthorized) when the <b>X-Requested-With</b> header equals <b>XMLHttpRequest</b>. Otherwise redirects to the
     * login page.
     */
    @Override
    public void commence(final HttpServletRequest request, final HttpServletResponse response, final AuthenticationException authException)
        throws IOException, ServletException {

        final String header = request.getHeader("X-Requested-With");
        final boolean isAjaxRequest = "XMLHttpRequest".equalsIgnoreCase(header);

        if (isAjaxRequest) {
            response.sendError(HttpStatus.UNAUTHORIZED.value());
        } else {
            super.commence(request, response, authException);
        }

    }

}

Um die Anpassung einzubinden, muss die Konfiguration wie folgt angepasst werden:

package de.patrickgotthard.security;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.AuthenticationEntryPoint;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private static final String LOGIN_PAGE = "/login";

    @Bean
    public AuthenticationEntryPoint authenticationEntryPoint() {
        return new AjaxAwareAuthenticationEntryPoint(LOGIN_PAGE);
    }

    @Override
    protected void configure(final HttpSecurity http) throws Exception {
        // @formatter:off
        http
            .authorizeRequests()
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .loginPage(LOGIN_PAGE)
                .permitAll()
                .and()
            .logout()
                .permitAll()
                .and()
            .exceptionHandling()
                .authenticationEntryPoint(this.authenticationEntryPoint());
        // @formatter:on
    }

}