In this article, I am going to provide a solution to configure Spring security without WebSecurityConfigurerAdapter class.
From Spring Security 5.7 onwards, the WebSecurityConfigurerAdapter class was deprecated and the Spring team encourages users to move towards a component-based security configuration.
With WebSecurityConfigurerAdapter
Before the WebSecurityConfigurerAdapter class was deprecated, we were writing code like this. We created a Spring Java configuration class, it extends the WebSecurityConfigurerAdapter class and overrides a couple of configure() methods:
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// configure HTTP security...
}
@Override
public void configure(WebSecurity web) throws Exception {
// configure Web security...
}
}
From Spring Security 5.7.0-M2 onwards. WebSecurityConfigurerAdapter class is deprecated and the Spring team encourages users to move towards a component-based security configuration.
Without WebSecurityConfigurerAdapter
In a new approach using component-based spring security configuration, you need to follow these very simple steps:
1. Remove WebSecurityConfigurerAdapter class (don't extend WebSecurityConfigurerAdapter)
2. Remove all the overridden methods of WebSecurityConfigurerAdapter class
3. Configure HttpSecurity using SecurityFilterChain and WebSecurity using WebSecurityCustomizer:
@Configuration
@EnableWebSecurity
public class SecurityConfiguration {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// configure HTTP security...
}
@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
// configure Web security...
}
}
Let's take a look at a complete example for your reference.
Spring Security without WebSecurityConfigurerAdapter Example
Consider we have below spring security configuration using the WebSecurityConfigurerAdapter class and later we will see how to migrate this security configuration towards a component-based approach.
package net.javaguides.springboot.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.dao.DaoAuthenticationProvider; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import net.javaguides.springboot.service.UserService; @Configuration @EnableWebSecurity public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Autowired private UserService userService; @Bean public BCryptPasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public DaoAuthenticationProvider authenticationProvider() { DaoAuthenticationProvider auth = new DaoAuthenticationProvider(); auth.setUserDetailsService(userService); auth.setPasswordEncoder(passwordEncoder()); return auth; } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(authenticationProvider()); } @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests().antMatchers( "/registration**", "/js/**", "/css/**", "/img/**").permitAll() .anyRequest().authenticated() .and() .formLogin() .loginPage("/login") .permitAll() .and() .logout() .invalidateHttpSession(true) .clearAuthentication(true) .logoutRequestMatcher(new AntPathRequestMatcher("/logout")) .logoutSuccessUrl("/login?logout") .permitAll(); } }
Next, here is the alternative component-based approach without WebSecurityConfigurerAdapter:
package net.javaguides.springboot.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; @Configuration @EnableWebSecurity public class SpringSecurity { // @Autowired // private UserDetailsService userDetailsService; @Bean public static PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } // configure SecurityFilterChain @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .antMatchers("/register/**").permitAll() .antMatchers("/index").permitAll() .antMatchers("/users").hasRole("ADMIN") .and() .formLogin( form -> form .loginPage("/login") .loginProcessingUrl("/login") .defaultSuccessUrl("/users") .permitAll() ).logout( logout -> logout .logoutRequestMatcher(new AntPathRequestMatcher("/logout")) .permitAll() ); return http.build(); } // @Autowired // public void configureGlobal(AuthenticationManagerBuilder builder) throws Exception { // builder.userDetailsService(userDetailsService) // .passwordEncoder(passwordEncoder()); // } }
Important: We no longer necessary to manually set UserDetailsService and PasswordEncoder to AuthenticationManager instance, it only needs to exist in the spring context. Once we configured UserDetailsService and PasswordEncoder as Spring beans then Spring Security automatically set to AuthenticationManager.
Spring Security JWT (JSON Web Token) without WebSecurityConfigurerAdapter
Consider we have below spring security and JWT configuration using the WebSecurityConfigurerAdapter class and later we will see how to migrate this security configuration towards a component-based approach.
package com.springboot.blog.config; import com.springboot.blog.security.CustomUserDetailsService; import com.springboot.blog.security.JwtAuthenticationEntryPoint; import com.springboot.blog.security.JwtAuthenticationFilter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.provisioning.InMemoryUserDetailsManager; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private CustomUserDetailsService userDetailsService; @Autowired private JwtAuthenticationEntryPoint authenticationEntryPoint; @Bean public JwtAuthenticationFilter jwtAuthenticationFilter(){ return new JwtAuthenticationFilter(); } @Bean PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } @Override protected void configure(HttpSecurity http) throws Exception { http .csrf().disable() .exceptionHandling() .authenticationEntryPoint(authenticationEntryPoint) .and() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() .antMatchers(HttpMethod.GET, "/api/v1/**").permitAll() .antMatchers("/api/v1/auth/**").permitAll() .antMatchers("/v2/api-docs/**").permitAll() .antMatchers("/swagger-ui/**").permitAll() .antMatchers("/swagger-resources/**").permitAll() .antMatchers("/swagger-ui.html").permitAll() .antMatchers("/webjars/**").permitAll() .anyRequest() .authenticated(); http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService) .passwordEncoder(passwordEncoder()); } @Override @Bean public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } // @Override // @Bean // protected UserDetailsService userDetailsService() { // UserDetails ramesh = User.builder().username("ramesh").password(passwordEncoder() // .encode("password")).roles("USER").build(); // UserDetails admin = User.builder().username("admin").password(passwordEncoder() // .encode("admin")).roles("ADMIN").build(); // return new InMemoryUserDetailsManager(ramesh, admin); // } }
Next, here is the alternative component-based approach without WebSecurityConfigurerAdapter:
package com.springboot.blog.config; import com.springboot.blog.security.CustomUserDetailsService; import com.springboot.blog.security.JwtAuthenticationEntryPoint; import com.springboot.blog.security.JwtAuthenticationFilter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.provisioning.InMemoryUserDetailsManager; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class SecurityConfig { @Autowired private CustomUserDetailsService userDetailsService; @Autowired private JwtAuthenticationEntryPoint authenticationEntryPoint; @Bean public JwtAuthenticationFilter jwtAuthenticationFilter(){ return new JwtAuthenticationFilter(); } @Bean PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } @Bean protected SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .csrf().disable() .exceptionHandling() .authenticationEntryPoint(authenticationEntryPoint) .and() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests((authorize) -> authorize .antMatchers(HttpMethod.GET, "/api/v1/**").permitAll() .antMatchers("/api/v1/auth/**").permitAll() .antMatchers("/v2/api-docs/**").permitAll() .antMatchers("/swagger-ui/**").permitAll() .antMatchers("/swagger-resources/**").permitAll() .antMatchers("/swagger-ui.html").permitAll() .antMatchers("/webjars/**").permitAll() .anyRequest() .authenticated() ); http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); return http.build(); } // @Override // protected void configure(HttpSecurity http) throws Exception { // http // .csrf().disable() // .exceptionHandling() // .authenticationEntryPoint(authenticationEntryPoint) // .and() // .sessionManagement() // .sessionCreationPolicy(SessionCreationPolicy.STATELESS) // .and() // .authorizeRequests() // .antMatchers(HttpMethod.GET, "/api/v1/**").permitAll() // .antMatchers("/api/v1/auth/**").permitAll() // .antMatchers("/v2/api-docs/**").permitAll() // .antMatchers("/swagger-ui/**").permitAll() // .antMatchers("/swagger-resources/**").permitAll() // .antMatchers("/swagger-ui.html").permitAll() // .antMatchers("/webjars/**").permitAll() // .anyRequest() // .authenticated(); // http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); // } @Bean public AuthenticationManager authenticationManager( AuthenticationConfiguration authenticationConfiguration) throws Exception { return authenticationConfiguration.getAuthenticationManager(); } // @Override // @Bean // public AuthenticationManager authenticationManagerBean() throws Exception { // return super.authenticationManagerBean(); // } // @Override // @Bean // protected UserDetailsService userDetailsService() { // UserDetails ramesh = User.builder().username("ramesh").password(passwordEncoder() // .encode("password")).roles("USER").build(); // UserDetails admin = User.builder().username("admin").password(passwordEncoder() // .encode("admin")).roles("ADMIN").build(); // return new InMemoryUserDetailsManager(ramesh, admin); // } }
Comments
Post a Comment
Leave Comment