spring security custom form login source tracking

Keywords: Spring Security

​ In the previous section, we tracked the source code of the default login page of security. You can refer to here: https://www.cnblogs.com/process-h/p/15522267.html In this section, let's take a look at how to customize the single table authentication page and source code tracking.

​ In order to implement custom forms and login pages, we need to write our own websecurityconfigure class, inherit the WebSecurityConfigurerAdapter object, and define our own login page path and failed jump path by overriding the configure method.

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests(authorizeRequests ->
                        authorizeRequests
                                .antMatchers("/css/**", "/index").permitAll()
                                .antMatchers("/user/**").hasRole("USER")
                )
                .formLogin(formLogin ->
                        formLogin
                                .loginPage("/login")
                                .failureUrl("/login-error")
                );
    }
    // @formatter:on

    @Bean
    public UserDetailsService userDetailsService() {
        UserDetails userDetails = User.withDefaultPasswordEncoder()
                .username("user")
                .password("password")
                .roles("USER")
                .build();
        return new InMemoryUserDetailsManager(userDetails);
    }
}

We implement the jump by introducing the Thymeleaf template

@Controller
public class MainController {

   @RequestMapping("/")
   public String root() {
      return "redirect:/index";
   }

   @RequestMapping("/index")
   public String index() {
      return "index";
   }

   @RequestMapping("/user/index")
   public String userIndex() {
      return "user/index";
   }

   @RequestMapping("/login")
   public String login() {
      return "login";
   }

   @RequestMapping("/login-error")
   public String loginError(Model model) {
      model.addAttribute("loginError", true);
      return "login";
   }

}

In the previous section, we mentioned the WebSecurityConfig class, which will have an init method

@Override
public void init(WebSecurity web) throws Exception {
   HttpSecurity http = getHttp();
   web.addSecurityFilterChainBuilder(http).postBuildAction(() -> {
      FilterSecurityInterceptor securityInterceptor = http.getSharedObject(FilterSecurityInterceptor.class);
      web.securityInterceptor(securityInterceptor);
   });
}

The HttpSecurity object is mentioned here. As the name suggests, its role is to ensure the security of HTTP requests. How does it ensure the security of HTTP requests? Let's look at the getHttp() method

protected final HttpSecurity getHttp() throws Exception {
   if (this.http != null) {
      return this.http;
   }
    // Initialize the authentication event publisher, that is, define the mapping relationship between some exceptions and the exception event class
   AuthenticationEventPublisher eventPublisher = getAuthenticationEventPublisher();
   this.localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);
    // Initialize authentication manager
   AuthenticationManager authenticationManager = authenticationManager();
   this.authenticationBuilder.parentAuthenticationManager(authenticationManager);
   Map<Class<?>, Object> sharedObjects = createSharedObjects();
   this.http = new HttpSecurity(this.objectPostProcessor, this.authenticationBuilder, sharedObjects);
   if (!this.disableDefaults) {
       // The configuration will be loaded by default
      applyDefaultConfiguration(this.http);
      ClassLoader classLoader = this.context.getClassLoader();
      List<AbstractHttpConfigurer> defaultHttpConfigurers = SpringFactoriesLoader
            .loadFactories(AbstractHttpConfigurer.class, classLoader);
      for (AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
         this.http.apply(configurer);
      }
   }
   configure(this.http);
   return this.http;
}

// Default authentication event publisher
public DefaultAuthenticationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
		this.applicationEventPublisher = applicationEventPublisher;
		addMapping(BadCredentialsException.class.getName(), AuthenticationFailureBadCredentialsEvent.class);
		addMapping(UsernameNotFoundException.class.getName(), AuthenticationFailureBadCredentialsEvent.class);
		addMapping(AccountExpiredException.class.getName(), AuthenticationFailureExpiredEvent.class);
		addMapping(ProviderNotFoundException.class.getName(), AuthenticationFailureProviderNotFoundEvent.class);
		addMapping(DisabledException.class.getName(), AuthenticationFailureDisabledEvent.class);
		addMapping(LockedException.class.getName(), AuthenticationFailureLockedEvent.class);
		addMapping(AuthenticationServiceException.class.getName(), AuthenticationFailureServiceExceptionEvent.class);
		addMapping(CredentialsExpiredException.class.getName(), AuthenticationFailureCredentialsExpiredEvent.class);
		addMapping("org.springframework.security.authentication.cas.ProxyUntrustedException",
				AuthenticationFailureProxyUntrustedEvent.class);
		addMapping("org.springframework.security.oauth2.server.resource.InvalidBearerTokenException",
				AuthenticationFailureBadCredentialsEvent.class);
	}

Let's take a look at the applyDefaultConfiguration method. As mentioned in the previous section, this is to configure some default configurations for httpSecurity objects. For example, csrf Cross Site Request Forgery protection will be enabled by default, webasyncmanagerintegration filter filter will be added, default login page will be added, and defaultloginpageconfigurator will be configured.

private void applyDefaultConfiguration(HttpSecurity http) throws Exception {
   http.csrf();
   http.addFilter(new WebAsyncManagerIntegrationFilter());
   http.exceptionHandling();
   http.headers();
   http.sessionManagement();
   http.securityContext();
   http.requestCache();
   http.anonymous();
   http.servletApi();
   http.apply(new DefaultLoginPageConfigurer<>());
   http.logout();
}

Back to the main method calling applyDefaultConfiguration(), after executing the if (!this.disableDefaults) {} branch, it will call its own configure(this.http); Method, that is, the method overridden in our custom WebSecurityConfig class, will execute our form login configuration policy.

.formLogin(formLogin ->
        formLogin
                .loginPage("/login")
                .failureUrl("/login-error")
);
@Override
public FormLoginConfigurer<H> loginPage(String loginPage) {
    return super.loginPage(loginPage);
}
protected T loginPage(String loginPage) {
    setLoginPage(loginPage);
    updateAuthenticationDefaults();
    this.customLoginPage = true;
    return getSelf();
}

Click the. loginPage("/login") method, and then click super.loginPage(loginPage); You can see that the login page has been rewritten and the custom login page flag has been written as true.

​ That's all for the custom form login page and source code tracking. During the process, we also found the filter order definition most closely related to security. In the construction method of the FilterOrderRegistration class, we defined the order of all filters that may be used in security. Interested readers can read it by themselves. The lines of login related source code are relatively thick. Next, it's time to look at the authentication and authorization parts.

Posted by blckspder on Wed, 10 Nov 2021 16:16:26 -0800