How to replace deprecated WebSecurityConfigurerAdapter in Spring Security 5.7.x

How to replace WebSecurityConfigurerAdapter

Spring Security deprecated WebSecurityConfigurerAdapter class in its 5.7.0 version as it encourages us to use component based configuration instead. However, this might confuse many people, since internet is full of examples for WebSecurityConfigurerAdapter. In this blog post, I show how to convert WebSecurityConfigurerAdapter configuration to component based configuration.

Here, you can see a security configuration class extends WebSecurityConfigurerAdapter as most Java developers used to see. It basically has a custom filter, two custom authentication provider and bunch of antMatcher() settings.

Let's convert this class to component based version.

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private JwtTokenFilter jwtTokenFilter;

    // Injecting JWT custom authentication provider
    @Autowired
    JwtAuthenticationProvider customAuthenticationProvider;

    // Injecting Google custom authentication provider
    @Autowired
    GoogleCloudAuthenticationProvider googleCloudAuthenticationProvider;

    @Bean
    public AuthenticationManager getAuthenticationManager() throws Exception {
        return super.authenticationManagerBean();
    }

    // adding our custom authentication providers
    // authentication manager will call these custom provider's
    // authenticate methods from now on.
    @Override
    protected void configure(AuthenticationManagerBuilder auth) {
        auth.authenticationProvider(customAuthenticationProvider)
            .authenticationProvider(googleCloudAuthenticationProvider);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                // disabling csrf since we won't use form login
                .csrf().disable()
                // giving every permission to every request for /login endpoint
                .authorizeRequests().antMatchers("/login").permitAll()
                // for everything else, the user has to be authenticated
                .anyRequest().authenticated()
                // setting stateless session, because we choose to implement Rest API
                .and().sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        // adding the custom filter before UsernamePasswordAuthenticationFilter in the filter chain
        http.addFilterBefore(jwtTokenFilter, UsernamePasswordAuthenticationFilter.class);
    }
}

1. Converting AuthenticationManager bean

Old version (Before Spring Security 5.7.0)

@Bean
public AuthenticationManager getAuthenticationManager() throws Exception {
    return super.authenticationManagerBean();
}

New version (After Spring Security 5.7.0)

@Bean
AuthenticationManager authenticationManager(
        AuthenticationConfiguration authenticationConfiguration) throws Exception {
    return authenticationConfiguration.getAuthenticationManager();
}

2. Converting multiple authentication provider setup

Old version (Before Spring Security 5.7.0)

@Override
protected void configure(AuthenticationManagerBuilder auth) {
    auth.authenticationProvider(customAuthenticationProvider)
        .authenticationProvider(googleCloudAuthenticationProvider);
}

New version (After Spring Security 5.7.0)

@Autowired
void registerProvider(AuthenticationManagerBuilder auth) {
    auth.authenticationProvider(customAuthenticationProvider)
        .authenticationProvider(googleCloudAuthenticationProvider);
}

3. Converting HttpSecurity setup

Old version (Before Spring Security 5.7.0)

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        // disabling csrf since we won't use form login
        .csrf().disable()
        // giving every permission to every request for /login endpoint
        .authorizeRequests().antMatchers("/login").permitAll()
        // for everything else, the user has to be authenticated
        .anyRequest().authenticated()
        // setting stateless session, because we choose to implement Rest API
        .and().sessionManagement()
        .sessionCreationPolicy(SessionCreationPolicy.STATELESS);

    // adding the custom filter before UsernamePasswordAuthenticationFilter in the filter chain
    http.addFilterBefore(jwtTokenFilter, UsernamePasswordAuthenticationFilter.class);
}

New version (After Spring Security 5.7.0)

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
        // disabling csrf since we won't use form login
        .csrf().disable()
        // giving permission to every request for /login endpoint
        .authorizeRequests().antMatchers("/login").permitAll()
        // for everything else, the user has to be authenticated
        .anyRequest().authenticated()
        // setting stateless session, because we choose to implement Rest API
        .and().sessionManagement()
        .sessionCreationPolicy(SessionCreationPolicy.STATELESS);

    // adding the custom filter before UsernamePasswordAuthenticationFilter in the filter chain
    http.addFilterBefore(jwtTokenFilter, UsernamePasswordAuthenticationFilter.class);
    return http.build();
}

Finally, security configuration should look like below. WebSecurityConfiguration does not extend WebSecurityConfigurerAdapter anymore, so I do not rely on WebSecurityConfigurerAdapter's methods. Instead, I create objects required for the configurations with @Bean annotation.

I also used @Autowired to access AuthenticationManagerBuilder, so I could set up my custom authentication providers.

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
class WebSecurityConfiguration { 

    @Autowired
    private JwtTokenFilter jwtTokenFilter;

    // Injecting JWT custom authentication provider
    @Autowired
    JwtAuthenticationProvider customAuthenticationProvider;

    // Injecting Google custom authentication provider
    @Autowired
    GoogleCloudAuthenticationProvider googleCloudAuthenticationProvider;

    @Bean
    AuthenticationManager authenticationManager(
        AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }

    // adding our custom authentication providers
    // authentication manager will call these custom provider's
    // authenticate methods from now on.
    @Autowired
    void registerProvider(AuthenticationManagerBuilder auth) {
        auth.authenticationProvider(customAuthenticationProvider)
            .authenticationProvider(googleCloudAuthenticationProvider);
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            // disabling csrf since we won't use form login
            .csrf().disable()
            // giving permission to every request for /login endpoint
            .authorizeRequests().antMatchers("/login").permitAll()
            // for everything else, the user has to be authenticated
            .anyRequest().authenticated()
            // setting stateless session, because we choose to implement Rest API
            .and().sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        // adding the custom filter before UsernamePasswordAuthenticationFilter in the filter chain
        http.addFilterBefore(jwtTokenFilter, UsernamePasswordAuthenticationFilter.class);
        return http.build();
    }
}

Conclusion

If you have any question regarding this blog post or any other content idea, please let me know in the comment section.

If you would like to see full working example for the code pieces I used in this blog post, you can see Github repository here.

backendstory/spring-security-upgrade at main · ugurcanlacin/backendstory
Contribute to ugurcanlacin/backendstory development by creating an account on GitHub.