Embedding several authentication schemes in the new Spring Security Config (Spring Boot 2.7+)

A Spring Security configuration class defines the configuration for restricting access to a spring web service only to authenticated and authorized users.
In Spring Boot 2.6 and earlier versions this was done via extending the WebSecurityConfigurerAdapter class and overriding abstract methods like

protected void configure(AuthenticationManagerBuilder auth) {…}
public void configure(WebSecurity web) throws Exception {…}

and embedding them in a spring security config class like this:

@Configuration

@EnableGlobalMethodSecurity(prePostEnabled = true)

public class MySecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                     .mvcMatchers(HttpMethod.GET, "/books/**").hasAnyRole("USER", "ADMIN")
                .mvcMatchers("/**").authenticated()
                .and()
            .httpBasic()
                .and()
            .csrf().disable();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
        …
    }
}

The new Spring Security configuration class, starting with Spring Boot 2.7 does not extend the WebSecurityConfigurerAdapter class anymore.
In this @Configuration class the principal method is @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception where the Spring Security filter chain is told which requests to filter. Several authentication schemes defined in separate beans can be added to the http.authorizeHttpRequests builder chain:

@EnableMethodSecurity
@Configuration
@Import(PasswordConfig.class)
public class MySecurityConfig {

       @Autowired
       private CustomUserDetailsService customUserDetailsService;

       @Autowired
       private PasswordEncoder passwordEncoder;

       @Autowired
       private CustomAuthenticationProvider customAuthenticationProvider;

       @Bean
       public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests((authz) -> authz
                .requestMatchers(HttpMethod.GET, "/books").hasAnyRole("USER", "ADMIN")
                .anyRequest().authenticated())
            .httpBasic(withDefaults())
            .userDetailsService(userDetailsService(passwordEncoder))
            .userDetailsService(customUserDetailsService)
            .authenticationProvider(customAuthenticationProvider)
            .csrf(CsrfConfigurer::disable);
        return http.build();
       }

       @Bean public InMemoryUserDetailsManager userDetailsService(PasswordEncoder passwordEncoder) {
             ...
        return new InMemoryUserDetailsManager(user, admin);
       }
}

A small final point: Place the PasswordEncoder @Bean in a separate configuration (defined in @Import(PasswordConfig.class)) as embedding this dependency as @Bean in the SecurityConfig runs into circular dependency issues!

@Configuration
public class PasswordConfig {
      @Bean public PasswordEncoder passwordEncoder() {
             return PasswordEncoderFactories.createDelegatingPasswordEncoder();
      }
}

Kommentar absenden

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert