I am trying to upgrade my old code(2 years old) to a newer project that is currently holding the latest version(Spring boot security V6.1.0) due to a serious vulnerability in its artifacts.
I'm trying to keep the same structure, but I am facing some difficulties with the Spring boot security framework.
In general, in each request, I am extracting a JWT token from the Authorization header, and running some logic to determine if the user is authorized or not.
This is my old code:
@EnableWebSecurity
public class SecurityConfigurer extends WebSecurityConfigurerAdapter {
@Autowired
private AuthenticateServiceImpl authenticateService;
@Autowired
private JwtRequestFilter jwtRequestFilter;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(authenticateService);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/auth/**").permitAll()
.antMatchers("/users/createUser").permitAll()//todo fix in the future
.antMatchers("/users/getProfile").permitAll()//todo fix in the future
.antMatchers("/users/**").hasRole( "ADMIN")
.antMatchers("/contacts/**").hasRole( "ADMIN")
.anyRequest().authenticated()
.and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
This is my new code for Spring-Security V6.1.0 which is currently making an issue with authenticationManagerBean
because the new class is not extending from WebSecurityConfigurerAdapter
(which is not exist any more).
@Configuration
@EnableWebSecurity
public class SecurityConfigurer{
@Autowired
private JwtRequestFilter jwtRequestFilter;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests((requests) -> requests
.requestMatchers("/auth/**").permitAll()
.requestMatchers("/users/createUser").permitAll()//todo fix in the future
.requestMatchers("/users/getProfile").permitAll()//todo fix in the future
.requestMatchers("/users/**").hasRole("ADMIN")
.requestMatchers("/contacts/**").hasRole("ADMIN")
.anyRequest().authenticated()
);
http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
/*@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}*/
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
How can I implement AuthenticationManager class, or finding an alternative for that because I am using this on my Auth services like this:
@RestController
@RequestMapping("/auth")
public class AuthController {
@Autowired
private AuthenticateServiceImpl authenticateService;
@Autowired
private Definitions definitions;
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private UserRepository userRepository;
@Autowired
private JwtUtil jwtUtil;
@Autowired
private UserServiceImpl userService;
@PostMapping(value = "/login")
public ResponseEntity<?> login(@RequestBody AuthenticationRequest authenticationRequest) throws UnauthorizedException {
try {
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(authenticationRequest.getUsername(), authenticationRequest.getPassword())
);
} catch (BadCredentialsException e) {
throw new UnauthorizedException(definitions.INVALID_CREDENTIAL);
}
final UserDetails userDetails = authenticateService
.loadUserByUsername(authenticationRequest.getUsername());
User user = userRepository.findUserByUsername(authenticationRequest.getUsername());
return ResponseEntity.ok()
.body(new AuthenticationResponse(jwtUtil.generateToken(userDetails), userService.convertToDtoRestricted(user)));
}
}
Just in case it's needed I am attaching the class JwtRequestFilter
:
@Component
@Slf4j
public class JwtRequestFilter extends OncePerRequestFilter {
@Autowired
private AuthenticateServiceImpl authenticateService;
@Autowired
private JwtUtil jwtUtil;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
final String authorizationHeader = request.getHeader("Authorization");
String username = null;
String jwt = null;
if (authorizationHeader != null) {
if (authorizationHeader.startsWith("Bearer ")) {
jwt = authorizationHeader.substring(7);
try {
username = jwtUtil.extractUsername(jwt);
} catch (SignatureException e) {
if(log.isInfoEnabled()) {
log.info("~JWT: Invalid Token ~ Access Denied!");
}
} catch (ExpiredJwtException e) {
if(log.isInfoEnabled()) {
log.info("~JWT: Expired Token ~ Access Denied!");
}
}
}
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.authenticateService.loadUserByUsername(username);
if (jwtUtil.validateToken(jwt, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
filterChain.doFilter(request, response);
}
}
Thanks for your help and time!
in your case you should create a
Global AuthenticationManager
In SecurityConfigurer you just will have following bean declaration:
@Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();}
And it will work great
P.S. Give me a feedback if it was helpfuly