sequence

This article describes how to customize the Login page of Spring Security. Most of the information on the web is outdated and based on back-end template technology, which is not very clear. This article provides a front and back end separation of login and return using Ajax.

Ajax returns

There are three areas that need to be handled: exception handling, which needs to be compatible with Ajax requests, success return handling, and failure 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");
        returnajaxFlag ! = null &&"XMLHttpRequest".equals(ajaxFlag); }}Copy the code

Here we’re customizing the ajax returns for success and failure, but here we’re simply going to return 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); }}Copy the code

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"); }}Copy the code

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"); }}Copy the code

Here are a few things to note:

  • PermitAll adds the front-end resource path and the interface address /login requested by the login form
  • LoginPage Sets the address of the login page. In this case, we use the static page, that is, login.html in the static directory
  • The Ajax configuration sets authenticationEntryPoint, successHandler, and failureHandler as the custom Ajax processing classes above

The login page

Is a pure HTML page with the following Ajax request for the login button:

$.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); }});Copy the code

This is the request /login path that Spring Security intercepts by default. For those of you who are not familiar with Spring Security, you may wonder if I requested this path, but there is no request mapping for /login in the project. Let’s take a look.

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

We need to focus on is the UsernamePasswordAuthenticationFilter here, as the name implies, it is a filter, when executed/login request to intercept, so it is no need to define the login request is in engineering mapping.

UsernamePasswordAuthenticationFilter

Spring ws-security – web – RELEASE 4.2.3 – sources. The 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);

        returnthis.getAuthenticationManager().authenticate(authRequest); } / /... }Copy the code

Here is the interception, taking the parameters submitted by login.html and passing them to the authenticationManager for authentication. The subsequent filter is followed. If successful, the corresponding session is configured.

doc

  • Spring Security dynamically configures URL permissions
  • Spring Security Notes: Customize Login/Logout Filter, AuthenticationProvider, AuthenticationToken