Spring securityax login and return

  Safety

Order

This article describes how to customize spring security’s login page. Most of the information given online is outdated and is based on back-end template technology. It is not very clear. This article gives a way to separate the front and back ends by ajax login and return.

Ajax return

A total of 3 places need to be handled. One is exception handling, which needs to be compatible with ajax requests. One is successful return handling and the other is failed return handling.

Ajax Exception Handling

public class UnauthorizedEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        if(isAjaxRequest(request)){
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED,authException.getMessage());
        }else{
            response.sendRedirect("/login.html");
        }

    }

    public static boolean isAjaxRequest(HttpServletRequest request) {
        String ajaxFlag = request.getHeader("X-Requested-With");
        return ajaxFlag != null && "XMLHttpRequest".equals(ajaxFlag);
    }
}

Here we customize the successful and failed ajax returns, of course here we simply deal with it and only return the statusCode

AjaxAuthSuccessHandler

public class AjaxAuthSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        response.setStatus(HttpServletResponse.SC_OK);
    }
}

AjaxAuthFailHandler

public class AjaxAuthFailHandler extends SimpleUrlAuthenticationFailureHandler {
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authentication failed");
    }
}

Security configuration

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .exceptionHandling().authenticationEntryPoint(new UnauthorizedEntryPoint())
                .and()
                .csrf().disable()
                .authorizeRequests()
                .antMatchers("/login","/css/**", "/js/**","/fonts/**").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login.html")
                .loginProcessingUrl("/login")
                .usernameParameter("name")
                .passwordParameter("password")
                .successHandler(new AjaxAuthSuccessHandler())
                .failureHandler(new AjaxAuthFailHandler())
                .permitAll()
                .and()
                .logout()
                .logoutUrl("/logout")
                .permitAll();
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .inMemoryAuthentication()
                .withUser("admin").password("admin").roles("USER");
    }
}

Here are a few points to note:

  • permitAll

Here, add the front-end resource path and the interface address /login of the login form request.

  • loginPage

The address of the login page is set here, and here we use the static page, that is, the login.html under the static directory.

  • Ajax configuration

Set authenticationEntryPoint, successHandler, failureHandler as the ajax processing class customized above

Login page

Is a pure html page, where the ajax request for the login button is as follows:

$.ajax({
            url: '/login',
            type: 'POST',
            data: "name="+name+"&password="+password,
            success: function (res, status) {
                window.location.href='/ok.html'
            },
            error: function (res, status) {
                dangerDialog(res.statusText);
            }
        });

Here is the request /login, which is the path spring security will intercept by default. People who do not know spring security may wonder, I requested this path, but there is no request mapping for /login defined in the project, does it matter? Let’s take a closer look.

Various filter built into spring security:

Alias Filter Class Namespace Element or Attribute
CHANNEL_FILTER ChannelProcessingFilter http/intercept-url@requires-channel
SECURITY_CONTEXT_FILTER SecurityContextPersistenceFilter http
CONCURRENT_SESSION_FILTER ConcurrentSessionFilter session-management/concurrency-control
HEADERS_FILTER HeaderWriterFilter http/headers
CSRF_FILTER CsrfFilter http/csrf
LOGOUT_FILTER LogoutFilter http/logout
X509_FILTER X509AuthenticationFilter http/x509
PRE_AUTH_FILTER AbstractPreAuthenticatedProcessingFilter Subclasses N/A
CAS_FILTER CasAuthenticationFilter N/A
FORM_LOGIN_FILTER UsernamePasswordAuthenticationFilter http/form-login
BASIC_AUTH_FILTER BasicAuthenticationFilter http/http-basic
SERVLET_API_SUPPORT_FILTER SecurityContextHolderAwareRequestFilter http/@servlet-api-provision
JAAS_API_SUPPORT_FILTER JaasApiIntegrationFilter http/@jaas-api-provision
REMEMBER_ME_FILTER RememberMeAuthenticationFilter http/remember-me
ANONYMOUS_FILTER AnonymousAuthenticationFilter http/anonymous
SESSION_MANAGEMENT_FILTER SessionManagementFilter session-management
EXCEPTION_TRANSLATION_FILTER ExceptionTranslationFilter http
FILTER_SECURITY_INTERCEPTOR FilterSecurityInterceptor http
SWITCH_USER_FILTER SwitchUserFilter N/A

What we need to pay attention to here is this usernamepasswordauthenticationfilter, which, as its name implies, is a filter that is intercepted when executing /login requests, so there is no need to define login’s request mapping in the project.

UsernamePasswordAuthenticationFilter

spring-security-web-4.2.3.RELEASE-sources.jar! /org/springframework/security/web/authentication/UsernamePasswordAuthenticationFilter.java

public class UsernamePasswordAuthenticationFilter extends
        AbstractAuthenticationProcessingFilter {

public Authentication attemptAuthentication(HttpServletRequest request,
            HttpServletResponse response) throws AuthenticationException {
        if (postOnly && !request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException(
                    "Authentication method not supported: " + request.getMethod());
        }

        String username = obtainUsername(request);
        String password = obtainPassword(request);

        if (username == null) {
            username = "";
        }

        if (password == null) {
            password = "";
        }

        username = username.trim();

        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
                username, password);

        // Allow subclasses to set the "details" property
        setDetails(request, authRequest);

        return this.getAuthenticationManager().authenticate(authRequest);
    }
    //......
}

This is interception, obtaining the parameters submitted by login.html , and then submitting them to authenticationManager for authentication. After that, follow-up filter will be followed. If successful, corresponding session configuration will be carried out.

doc