Starting from Spring Security version 5.7.0-M2 the WebSecurityConfigurerAdapter is deprecated. In this blog post you will find a complete code example that demonstrates how to configure HttpSecurity object to make it support User Authentication and User Authorization.
Before
Below is a code example that uses WebSecurityConfigurerAdapter.
@EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class WebSecurity extends WebSecurityConfigurerAdapter{ ... @Override protected void configure(HttpSecurity http) throws Exception { http .cors().and() .csrf().disable().authorizeRequests() .antMatchers(HttpMethod.POST, SecurityConstants.SIGN_UP_URL) .permitAll() .anyRequest().authenticated().and() .addFilter(getAuthenticationFilter()) .addFilter(new AuthorizationFilter(authenticationManager(), userRepository)) .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS); http.headers().frameOptions().disable(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder); } protected AuthenticationFilter getAuthenticationFilter() throws Exception { final AuthenticationFilter filter = new AuthenticationFilter(authenticationManager()); filter.setFilterProcessesUrl("/users/login"); return filter; } }
After
Below is a code example that does not use WebSecurityConfigurerAdapter anymore.
@EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class WebSecurity { ... @Bean public SecurityFilterChain configure(HttpSecurity http) throws Exception { // Configure AuthenticationManagerBuilder AuthenticationManagerBuilder authenticationManagerBuilder = http.getSharedObject(AuthenticationManagerBuilder.class); authenticationManagerBuilder.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder); // Get AuthenticationManager AuthenticationManager authenticationManager = authenticationManagerBuilder.build(); http .cors().and() .csrf().disable().authorizeRequests() .antMatchers(HttpMethod.POST, SecurityConstants.SIGN_UP_URL) .permitAll() .anyRequest().authenticated().and() .addFilter(getAuthenticationFilter(authenticationManager)) .addFilter(new AuthorizationFilter(authenticationManager, userRepository)) .authenticationManager(authenticationManager) .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS); http.headers().frameOptions().disable(); return http.build(); } protected AuthenticationFilter getAuthenticationFilter(AuthenticationManager authenticationManager) throws Exception { final AuthenticationFilter filter = new AuthenticationFilter(authenticationManager); filter.setFilterProcessesUrl("/users/login"); return filter; } }
Complete Example of WebSecurity Without WebSecurityConfigurerAdapter
Below is a complete code example of WebSecurity class that does not use the deprecated WebSecurityConfigurerAdapter.
You will probably notice a couple of main differences:
- Notice that the configure(HttpSecurity http) method is annotated with @Bean annotation,
- I have also configured the AuthenticationManagerBuilder inside of the configure(HttpSecurity http) method.
package com.appsdeveloperblog.app.ws.security; import com.appsdeveloperblog.app.ws.io.repository.UserRepository; import java.util.Arrays; import org.springframework.context.annotation.Bean; import org.springframework.http.HttpMethod; 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.http.SessionCreationPolicy; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import com.appsdeveloperblog.app.ws.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.web.SecurityFilterChain; @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class WebSecurity { private final UserService userDetailsService; private final BCryptPasswordEncoder bCryptPasswordEncoder; private final UserRepository userRepository; @Autowired public WebSecurity(@Qualifier("userService") UserService userDetailsService, BCryptPasswordEncoder bCryptPasswordEncoder, UserRepository userRepository) { this.userDetailsService = userDetailsService; this.bCryptPasswordEncoder = bCryptPasswordEncoder; this.userRepository = userRepository; } @Bean public SecurityFilterChain configure(HttpSecurity http) throws Exception { // Configure AuthenticationManagerBuilder AuthenticationManagerBuilder authenticationManagerBuilder = http.getSharedObject(AuthenticationManagerBuilder.class); authenticationManagerBuilder.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder); // Get AuthenticationManager AuthenticationManager authenticationManager = authenticationManagerBuilder.build(); http .cors().and() .csrf().disable().authorizeRequests() .antMatchers(HttpMethod.POST, SecurityConstants.SIGN_UP_URL) .permitAll() .antMatchers(SecurityConstants.H2_CONSOLE) .permitAll() .anyRequest().authenticated().and() // User Authentication with custom login URL path .addFilter(getAuthenticationFilter(authenticationManager)) // User Authorization with JWT .addFilter(new AuthorizationFilter(authenticationManager, userRepository)) .authenticationManager(authenticationManager) .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS); http.headers().frameOptions().disable(); return http.build(); } // Configure custom login URL path protected AuthenticationFilter getAuthenticationFilter(AuthenticationManager authenticationManager) throws Exception { final AuthenticationFilter filter = new AuthenticationFilter(authenticationManager); filter.setFilterProcessesUrl("/users/login"); return filter; } }
AuthorizationFilter class
Below is a code example of AuthorizationFilter class that will be used to validate the provided Authorization JWT.
package com.appsdeveloperblog.app.ws.security; import com.appsdeveloperblog.app.ws.io.entity.UserEntity; import com.appsdeveloperblog.app.ws.io.repository.UserRepository; import java.io.IOException; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; import io.jsonwebtoken.Jwts; public class AuthorizationFilter extends BasicAuthenticationFilter { UserRepository userRepository; public AuthorizationFilter(AuthenticationManager authManager, UserRepository userRepository) { super(authManager); this.userRepository = userRepository; } @Override protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException { String header = req.getHeader(SecurityConstants.HEADER_STRING); if (header == null || !header.startsWith(SecurityConstants.TOKEN_PREFIX)) { chain.doFilter(req, res); return; } UsernamePasswordAuthenticationToken authentication = getAuthentication(req); SecurityContextHolder.getContext().setAuthentication(authentication); chain.doFilter(req, res); } private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) { String token = request.getHeader(SecurityConstants.HEADER_STRING); if (token != null) { token = token.replace(SecurityConstants.TOKEN_PREFIX, ""); String user = Jwts.parser() .setSigningKey( SecurityConstants.getTokenSecret() ) .parseClaimsJws( token ) .getBody() .getSubject(); if (user != null) { UserEntity userEntity = userRepository.findByEmail(user); return new UsernamePasswordAuthenticationToken(user, null, new UserPrincipal(userEntity).getAuthorities()); } return null; } return null; } }
UserService Interface Implementation
The above code example makes use of UserRepository and UserServiceImpl classes. Below is an example of UserServiceImpl class.
package com.appsdeveloperblog.app.ws.service.impl; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; import com.appsdeveloperblog.app.ws.io.entity.UserEntity; import com.appsdeveloperblog.app.ws.io.repository.UserRepository; import com.appsdeveloperblog.app.ws.service.UserService; @Service("userService") public class UserServiceImpl implements UserService { @Autowired UserRepository userRepository; @Autowired BCryptPasswordEncoder bCryptPasswordEncoder; @Override public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException { UserEntity userEntity = userRepository.findByEmail(email); if (userEntity == null) throw new UsernameNotFoundException(email); return new User(userEntity.getEmail(), userEntity.getEncryptedPassword(), new ArrayList<>()); } }
Main Application file
Below is a code example of main application file that also creates an instance of BCryptPasswordEncoder.
package com.appsdeveloperblog.app.ws; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; @SpringBootApplication public class MobileAppWsApplication { public static void main(String[] args) { SpringApplication.run(MobileAppWsApplication.class, args); } @Bean public BCryptPasswordEncoder bCryptPasswordEncoder() { return new BCryptPasswordEncoder(); } }
I hope this code example was of some help to you. Let me know if you have questions in the comments below.
Happy learning!