Issue
I am trying to update my application without the use of WebSecurityConfigurerAdapter and I need help. The code is before and after the changes. I don't know for sure about the authenticationManager. I used multiple website to refactor my code.
Before:
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private CustomPasswordEncoder customPasswordEncoder;
@Autowired
private JwtFilter jwtFilter;
@Override @Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(customPasswordEncoder
.getPasswordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http = http.cors().and().csrf().disable();
// Set session management to stateless
http = http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and();
// Set unauthorized requests exception handler
http = http
.exceptionHandling()
.authenticationEntryPoint(
(request, response, ex) -> {
response.sendError(
HttpServletResponse.SC_UNAUTHORIZED,
ex.getMessage()
);
}
)
.and();
// Set permissions on endpoints
http.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.antMatchers("/swagger-ui/**").permitAll()
.antMatchers("/v3/api-docs/**").permitAll()
.antMatchers("/error**").permitAll()
.anyRequest().authenticated();
// Add JWT token filter
http.addFilterBefore(
jwtFilter,
UsernamePasswordAuthenticationFilter.class
);
}
}
After refactoring my code without the WebSecurityConfigurerAdapter:
@EnableWebSecurity
public class SecurityConfig{
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private CustomPasswordEncoder customPasswordEncoder;
@Autowired
private JwtFilter jwtFilter;
@Bean
AuthenticationManager authenticationManager(AuthenticationManagerBuilder builder) throws Exception {
return builder.userDetailsService(userDetailsService)
.passwordEncoder(customPasswordEncoder.getPasswordEncoder())
.and().build();
}
public SecurityFilterChain configure(HttpSecurity http) throws Exception {
http = http.cors().and().csrf().disable();
// Set session management to stateless
http = http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and();
// Set unauthorized requests exception handler
http = http
.exceptionHandling()
.authenticationEntryPoint(
(request, response, ex) -> {
response.sendError(
HttpServletResponse.SC_UNAUTHORIZED,
ex.getMessage()
);
}
)
.and();
// Set permissions on endpoints
http.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.antMatchers("/swagger-ui/**").permitAll()
.antMatchers("/v3/api-docs/**").permitAll()
.antMatchers("/error**").permitAll()
.anyRequest().authenticated();
// Add JWT token filter
http.addFilterBefore(
jwtFilter,
UsernamePasswordAuthenticationFilter.class
);
return http.build();
}
}
And Lastly this is my AuthController. After performing the changes in my SecurityConfig the Application stops working. With a huge error. "org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration': Unsatisfied dependency expressed through method 'setFilterChains' parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'defaultSecurityFilterChain' defined in class path resource [org/springframework/boot/autoconfigure/security/servlet/SpringBootWebSecurityConfiguration$SecurityFilterChainConfiguration.class]: Unsatisfied dependency expressed through method 'defaultSecurityFilterChain' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.config.annotation.web.configuration.HttpSecurityConfiguration.httpSecurity' defined in class path resource [org/springframework/security/config/annotation/web/configuration/HttpSecurityConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.security.config.annotation.web.builders.HttpSecurity]: Factory method 'httpSecurity' threw exception; nested exception is java.lang.IllegalStateException: Cannot apply org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration$EnableGlobalAuthenticationAutowiredConfigurer@53da2aec to already built object"
package io.qbeat.tmregistrationtool.web;
import io.jsonwebtoken.ExpiredJwtException;
import io.qbeat.tmregistrationtool.domain.User;
import io.qbeat.tmregistrationtool.dto.AuthCredentialsRequest;
import io.qbeat.tmregistrationtool.util.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseCookie;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/auth")
public class AuthController {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private JwtUtil jwtUtil;
private String domain;
@PostMapping("login")
public ResponseEntity <?> login (@RequestBody AuthCredentialsRequest request){
try {
Authentication authenticate = authenticationManager
.authenticate(
new UsernamePasswordAuthenticationToken(
request.getUsername(), request.getPassword()
)
);
User user = (User) authenticate.getPrincipal();
user.setPassword(null);
return ResponseEntity.ok()
.header(
HttpHeaders.AUTHORIZATION,
jwtUtil.generateToken(user)
)
.body(user);
} catch (BadCredentialsException ex) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
}
@GetMapping("/validate")
public ResponseEntity<?> validateToken(@RequestParam String token,
@AuthenticationPrincipal User user) {
try {
Boolean isValidToken = jwtUtil.validateToken(token, user);
return ResponseEntity.ok(isValidToken);
} catch (ExpiredJwtException e) {
return ResponseEntity.ok(false);
}
}
}
Any help will be much appreciated.
Solution
I finally managed to fix my issue. The issue was with the AuthenticationManager.
@Bean
public AuthenticationManager authenticationManagerBean(HttpSecurity http) throws Exception {
AuthenticationManagerBuilder authenticationManagerBuilder = http.getSharedObject(AuthenticationManagerBuilder.class);
authenticationManagerBuilder.userDetailsService(userDetailsService).passwordEncoder(customPasswordEncoder.getPasswordEncoder());
return authenticationManagerBuilder.build();
}
Answered By - Georgios Kompos
Answer Checked By - Gilberto Lyons (JavaFixing Admin)