Fix lỗi Is there an unresolvable circular reference?

package com.example.manager_student.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/register").permitAll()
                .antMatchers("/").hasRole("MEMBER")
                .antMatchers("/admin").hasRole("ADMIN")
                .and()
            .formLogin()
                .loginPage("/login")
                .usernameParameter("email")
                .passwordParameter("password")
                .defaultSuccessUrl("/")
                .failureUrl("/login?error")
                .and()
            .exceptionHandling()
                .accessDeniedPage("/403");
    }
}
package com.example.manager_student.config;

import java.util.HashSet;

import com.example.manager_student.domain.Role;
import com.example.manager_student.domain.User;
import com.example.manager_student.repository.RoleRepository;
import com.example.manager_student.repository.UserRepository;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;

@Component
public class DataSeedingListener implements ApplicationListener<ContextRefreshedEvent> {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private RoleRepository roleRepository;

    @Autowired 
    private PasswordEncoder passwordEncoder;

    @Override
    public void onApplicationEvent(ContextRefreshedEvent arg0) {
        // Roles
        if (roleRepository.findByName("ROLE_ADMIN") == null) {
            roleRepository.save(new Role("ROLE_ADMIN"));
        }

        if (roleRepository.findByName("ROLE_MEMBER") == null) {
            roleRepository.save(new Role("ROLE_MEMBER"));
        }

        // Admin account
        if (userRepository.findByEmail("[email protected]") == null) {
            User admin = new User();
            admin.setEmail("[email protected]");
            admin.setPassword(passwordEncoder.encode("123456"));
            HashSet<Role> roles = new HashSet<>();
            roles.add(roleRepository.findByName("ROLE_ADMIN"));
            roles.add(roleRepository.findByName("ROLE_MEMBER"));
            admin.setRole(roles);
            userRepository.save(admin);
        }

        // Member account
        if (userRepository.findByEmail("[email protected]") == null) {
            User user = new User();
            user.setEmail("[email protected]");
            user.setPassword(passwordEncoder.encode("123456"));
            HashSet<Role> roles = new HashSet<>();
            roles.add(roleRepository.findByName("ROLE_MEMBER"));
            user.setRole(roles);
            userRepository.save(user);
        }
    }
}

nó xuất thông báo lỗi
Error creating bean with name 'dataSeedingListener': Unsatisfied dependency expressed through field 'passwordEncoder'; 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?

mình coi lỗi thì nó báo bị circular import
như A import BB import A

2 đoạn code trên thì không có vụ đó. Có thể bạn bị lỗi ở khúc DI này

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
4 Likes

Vậy phải fix như thế nào

Cậu đang gộp security config với app config chung với nhau rồi @@
Khi đó, PasswordEncoder của cậu được tạo trong cả app config & Security config.
Cậu cần tách ra 2 class riêng:

@Configuration
public class AppConfig {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}


@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/register").permitAll()
                .antMatchers("/").hasRole("MEMBER")
                .antMatchers("/admin").hasRole("ADMIN")
                .and()
            .formLogin()
                .loginPage("/login")
                .usernameParameter("email")
                .passwordParameter("password")
                .defaultSuccessUrl("/")
                .failureUrl("/login?error")
                .and()
            .exceptionHandling()
                .accessDeniedPage("/403");
    }
}

Thử xem nhé! :smile:

3 Likes

vậy thì sẽ xuất hiện vấn đề mới là passwordEncoder() không có khai báo

Cậu chắc là đã tạo class AppConfig chứa passwordEncoder bean chưa thế? :smile:
Sẽ tốt hơn nếu cậu để class đó chung package với class WebSecurityConfig ban đầu của cậu.

2 Likes

Mình cũng tạo appConfig và chung package
nó thông báo lỗi:
The method passwordEncoder() is undefined for the type WebSecurityConfig

Hm.
Vậy thì cậu thử rollback về version đầu tiên của cậu, và bỏ annotation @Configuration đi xem.
Như tớ đã để cập ở comment trước, cả 2 annotation cậu đề cập ở WebSecurityConfig sẽ được load lần lượt, dẫn tới passwordEncoder được load 2 lần => nếu tớ không nhầm, đây là lý do cậu nhận được thông báo lỗi “hình như có phụ thuộc vòng ở đây”.

2 Likes

Mình đã fix được rồi, cảm ơn đã hỗ trợ

AppConfig appConfig = new AppConfig();

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(appConfig.passwordEncoder());
    }
3 Likes

A post was merged into an existing topic: Topic lưu trữ các post off-topic - version 3

83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?