I am trying to add Login and Registration to my API Application. But I ran into problems. Two beans conflict with each other. I put all classes which interact with these beans in the message.
2024-03-11T00:13:22.768+03:00 WARN 22820 --- [ main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'securityController': Unsatisfied dependency expressed through method 'setAuthenticationManager' parameter 0: Error creating bean with name 'authenticationManager' defined in class path resource [com/weather/weather/config/SecurityConfigurator.class]: Failed to instantiate [org.springframework.security.authentication.AuthenticationManager]: Factory method 'authenticationManager' threw exception with message: No qualifying bean of type 'org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder' available: expected single matching bean but found 2: authenticationManagerBuilder,configureAuthenticationManagerBuilder
2024-03-11T00:13:22.769+03:00 INFO 22820 --- [ main] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2024-03-11T00:13:22.771+03:00 INFO 22820 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2024-03-11T00:13:22.774+03:00 INFO 22820 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
2024-03-11T00:13:22.776+03:00 INFO 22820 --- [ main] o.apache.catalina.core.StandardService : Stopping service [Tomcat]
2024-03-11T00:13:22.788+03:00 INFO 22820 --- [ main] .s.b.a.l.ConditionEvaluationReportLogger :
Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled.
2024-03-11T00:13:22.800+03:00 ERROR 22820 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of method setAuthenticationManager in com.weather.weather.controller.SecurityController required a single bean, but 2 were found:
- authenticationManagerBuilder: defined by method 'authenticationManagerBuilder' in class path resource [org/springframework/security/config/annotation/authentication/configuration/AuthenticationConfiguration.class]
- configureAuthenticationManagerBuilder: defined by method 'configureAuthenticationManagerBuilder' in class path resource [com/weather/weather/config/SecurityConfigurator.class]
This may be due to missing parameter name information
Action:
Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed
Ensure that your compiler is configured to use the '-parameters' flag.
You may need to update both your build tool settings as well as your IDE.
SecurityConfigurator
@Configuration
@EnableWebSecurity
@Data
public class SecurityConfigurator {
private UserService userService;
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration)throws Exception{
return authenticationConfiguration.getAuthenticationManager();
}
@Bean
public AuthenticationManagerBuilder configureAuthenticationManagerBuilder(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder.userDetailsService(userService).passwordEncoder(passwordEncoder());
return authenticationManagerBuilder;
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.cors(AbstractHttpConfigurer::disable)
.cors(httpSecurityCorsConfigurer ->
httpSecurityCorsConfigurer.configurationSource(request ->
new CorsConfiguration().applyPermitDefaultValues())
)
.exceptionHandling(exceptions -> exceptions.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)))
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/auth/**").permitAll()
.requestMatchers("/api/v1/weather").fullyAuthenticated()
.anyRequest().permitAll()
);
return http.build();
}
}
SecurityController
@RestController
@RequestMapping("/auth")
public class SecurityController {
private UserRepository userRepository;
private PasswordEncoder passwordEncoder;
private AuthenticationManager authenticationManager;
private JwtCore jwtCore;
@Autowired
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Autowired
public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
this.passwordEncoder = passwordEncoder;
}
@Autowired
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
@Autowired
public void setJwtCore(JwtCore jwtCore) {
this.jwtCore = jwtCore;
}
@PostMapping("/signup")
ResponseEntity<?> signup(@RequestBody SignUpRequest signUpRequest){
if(userRepository.existsUserByUsername(signUpRequest.getUsername())){
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("This nickname already exist");
}
User user = new User();
user.setUsername(signUpRequest.getUsername());
user.setPassword(passwordEncoder.encode(signUpRequest.getPassword()));
userRepository.save(user);
return ResponseEntity.ok("Chinazes");
}
@PostMapping("/signin")
ResponseEntity<?> signin(@RequestBody SignInRequest signInRequest){
Authentication authentication = null;
try{
authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(signInRequest.getUsername(),signInRequest.getPassword()));
} catch (BadCredentialsException e){
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
}
SecurityContextHolder.getContext().setAuthentication(authentication);
String jwt = jwtCore.generateToken(authentication);
return ResponseEntity.ok(jwt);
}
}
I tried to use @Primary and @Qualifier, but they caused more errors. I don't understand why Spring complains about it, if these beans are different.
The issue lies in the fact that the
@EnableWebSecuritycreates anAuthenticationManagerBuilderbean, and you do as well. I confirmed locally that if you mark the AuthenticationManagerBuilder with@Primarythe error goes away as spring boot is now able to determine which bean to use in yourSecurityController.