I get the following error whilst trying to run the application. Any help is appreciated.
"Error starting Tomcat context. Exception: org.springframework.beans.factory.UnsatisfiedDependencyException. Message: Error creating bean with name 'webSecurityConfig': Unsatisfied dependency expressed through field 'userServiceImpl'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userServiceImpl': Unsatisfied dependency expressed through field 'bCryptEncoder'; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'webSecurityConfig': Requested bean is currently in creation: Is there an unresolvable circular reference?"
2 related files for the issue are as follows:
WebSecurityConfig
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserServiceImpl userServiceImpl;
private JwtTokenUtil jwtTokenUtil;
private AuthEntryPoint unauthorizedHandler;
public WebSecurityConfig(AuthEntryPoint unauthorizedHandler, JwtTokenUtil jwtTokenUtil) {
this.unauthorizedHandler = unauthorizedHandler;
this.jwtTokenUtil = jwtTokenUtil;
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Autowired
public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userServiceImpl)
.passwordEncoder(encoder());
}
@Bean
public AuthenticationFilter authenticationTokenFilterBean() throws Exception {
return new AuthenticationFilter(userServiceImpl, jwtTokenUtil);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.csrf()
.disable()
.authorizeRequests()
.antMatchers(
"/auth/*",
"/token/*",
"/webjars/**",
"/",
"/uploads/**",
"favicon.ico"
).permitAll()
.anyRequest().authenticated()
.and()
.exceptionHandling()
.authenticationEntryPoint(unauthorizedHandler)
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
}
@Bean
public BCryptPasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
}
UserServiceImpl
@Service
public class UserServiceImpl implements UserService {
private final UserRepository userRepository;
@Autowired
private BCryptPasswordEncoder bCryptEncoder; // Fails when injected by the constructor.
public UserServiceImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}
/**
* Create a new user.
* @param createUserDto
* @return
*/
@Override
public User save(CreateUserDto createUserDto) {
User newUser = new User();
newUser.setEmail(createUserDto.getEmail());
newUser.setFullName(createUserDto.getFullName());
newUser.setPassword(bCryptEncoder.encode(createUserDto.getPassword()));
newUser.setConfirmed(createUserDto.isConfirmed());
newUser.setEnabled(createUserDto.isEnabled());
newUser.setRole(createUserDto.getRole());
return userRepository.save(newUser);
}
@Override
public List<User> findAll() {
List<User> list = new ArrayList<>();
userRepository.findAll().iterator().forEachRemaining(list::add);
return list;
}
@Override
public void delete(String id) {
userRepository.deleteById(id);
}
@Override
public User findByEmail(String email) throws ResourceNotFoundException {
Optional<User> optionalUser = userRepository.findByEmail(email);
if (optionalUser.isEmpty()) {
throw new ResourceNotFoundException(USER_NOT_FOUND_MESSAGE);
}
return optionalUser.get();
}
@Override
public User findById(String id) throws ResourceNotFoundException {
Optional<User> optionalUser = userRepository.findById(id);
if (optionalUser.isEmpty()) {
throw new ResourceNotFoundException(USER_NOT_FOUND_MESSAGE);
}
return optionalUser.get();
}
@Override
public User update(String id, UpdateUserDto updateUserDto) throws ResourceNotFoundException {
User user = findById(id);
if (updateUserDto.getFullName() != null) {
user.setFullName(updateUserDto.getFullName());
}
if (updateUserDto.getEmail() != null) {
user.setEmail(updateUserDto.getEmail());
}
return userRepository.save(user);
}
@Override
public void update(User user) {
userRepository.save(user);
}
@Override
public User updatePassword(String id, UpdatePasswordDto updatePasswordDto) throws ResourceNotFoundException {
User user = findById(id);
if (bCryptEncoder.matches(updatePasswordDto.getCurrentPassword(), user.getPassword())) {
user.setPassword(bCryptEncoder.encode(updatePasswordDto.getNewPassword()));
return userRepository.save(user);
}
return null;
}
@Override
public void updatePassword(String id, String newPassword) throws ResourceNotFoundException {
User user = findById(id);
user.setPassword(bCryptEncoder.encode(newPassword));
userRepository.save(user);
}
public void confirm(String id) throws ResourceNotFoundException {
User user = findById(id);
user.setConfirmed(true);
userRepository.save(user);
}
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Optional<User> userOptional = userRepository.findByEmail(username);
if(userOptional.isEmpty()){
throw new UsernameNotFoundException("Invalid username or password.");
}
User user = userOptional.get();
return new org.springframework.security.core.userdetails.User(
user.getEmail(), user.getPassword(), user.isEnabled(), true, true, user.isConfirmed(), getAuthority(user)
);
}
private Set<SimpleGrantedAuthority> getAuthority(User user) {
Set<SimpleGrantedAuthority> authorities = new HashSet<>();
authorities.add(new SimpleGrantedAuthority(user.getRole().getName()));
user.allPermissions().forEach(permission -> authorities.add(new SimpleGrantedAuthority(permission.getName())));
return authorities;
}
}
CodePudding user response:
You can try like this:
@Service
public class UserServiceImpl implements UserService {
private final UserRepository userRepository;
private final BCryptPasswordEncoder bCryptEncoder;
public UserServiceImpl(UserRepository userRepository, BCryptPasswordEncoder bCryptEncoder) {
this.userRepository = userRepository;
this.bCryptEncoder = bCryptEncoder;
}
[...]
CodePudding user response:
I believe a class annotated with @Configuration will be subjected to executed initially in a Spring application. As we can see you have created a lot of beans that has to be autowired in multiple other classes which will be created eventually probably the classes annotated with service, controller and all.
Now in the WebSecurityConfig
there is an autowiring for UserServiceImpl
for which the bean hasn't created yet it will be created after the configuration class is done. And the UserServiceImpl
also requires BCryptPasswordEncoder
So this appears like a dead lock each of them is dependent on one another. This is what I could decipher.
Also autowiring should be done with Interface as type.
@Autowired
private UserService userServiceImpl;
I think moving the password encoder bean creation to another config class might help to solve this situation.