Home > Blockchain >  Java Spring Boot v2.6.4 - circular reference issue
Java Spring Boot v2.6.4 - circular reference issue

Time:06-17

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.

  • Related