1. Preface

In previous Spring Security tutorials we customize configuration by declaring a configuration class WebSecurityConfigurerAdapter and then overriding (@Override) the corresponding methods. However, all this has changed since Spring Security 5.4, since Spring Security 5.4 we don’t need to inherit from WebSecurityConfigurerAdapter in order to configure HttpSecurity. The original description reads.

  • Remove need for WebSecurityConfigurerAdapter #8805
  • Configure HTTP Security without extending WebSecurityConfigurerAdapter #8804 issues/8804)

2. The new configuration method

The old way of configuration is still valid.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@Configuration
static class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .antMatcher("/**")
            .authorizeRequests(authorize -> authorize
                    .anyRequest().authenticated()
            );
    }
}

However 5.4.x version we have new options.

1
2
3
4
5
6
7
8
9
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    return http
            .antMatcher("/**")
            .authorizeRequests(authorize -> authorize
                    .anyRequest().authenticated()
            )
            .build();
}

This JavaConfig approach looks more fresh and comfortable, and decoupled from the adapter. Wait I think I found something new, the parameter of the filterChain method above is of type HttpSecurity. Students familiar with the @Bean annotation should realize that there must be a Spring Bean of type HttpSecurity. That’s right! There is one such Bean in the HttpSecurityConfiguration.

1
2
3
4
5
6
@Bean({"org.springframework.security.config.annotation.web.configuration.HttpSecurityConfiguration.httpSecurity"})
@Scope("prototype")
HttpSecurity httpSecurity() throws Exception {
    // 省略掉
    return http;
}

The initialization has been ignored and it is not the focus of this article. We notice that HttpSecurity is tagged by @Scope("prototype"). That is, the HttpSecurity bean is not a singleton, and a new instance is constructed with each request. This setting is very convenient for us to build multiple SecurityFilterChains that are not much related to each other, and thus we can build mutually isolated security policies in a security system. For example, Session mode is used for the back-end management platform, and Token mode is used for the front-end application.

SecurityFilterChain

3. Principle

Spring Security has a default filter chain class called springSecurityFilterChain (the actual location is the Bean Filter location in the above image) of type FilterChainProxy, which proxies all SecurityFilterChain, and the key proxy injection code is as follows.

1
2
3
4
5
6
7
8
9
for (SecurityFilterChain securityFilterChain : this.securityFilterChains) {
   this.webSecurity.addSecurityFilterChainBuilder(() -> securityFilterChain);
   for (Filter filter : securityFilterChain.getFilters()) {
      if (filter instanceof FilterSecurityInterceptor) {
         this.webSecurity.securityInterceptor((FilterSecurityInterceptor) filter);
         break;
      }
   }
}

So where does this.securityFilterChains come from?

1
2
3
4
5
@Autowired(required = false)
void setFilterChains(List<SecurityFilterChain> securityFilterChains) {
   securityFilterChains.sort(AnnotationAwareOrderComparator.INSTANCE);
   this.securityFilterChains = securityFilterChains;
}

As you can see here, beans of type SecurityFilterChain are loaded into this.securityFilterChains. If you have upgraded your version of Spring Security to 5.4.x, you can try this approach.

Reference https://felord.cn/spring-security-5.4-new.html