programing

스프링 시큐리티 원형 콩 의존성

powerit 2023. 10. 10. 21:16
반응형

스프링 시큐리티 원형 콩 의존성

저는 현재 봄에 바딘 어플을 만들고 있습니다.앱 사양에 따라 다음을 통해 데이터베이스를 조회하여 사용자의 인증/인증을 완료해야 합니다.jdbcTemplate. 이 문제를 해결하는 방법은 무엇입니까?Spring Boot 1.4.2를 사용하고 있습니다.풀어주다.

업데이트: 이 방법은 Spring Boot 1.1.x에서 작동합니다.그러나 RELEASE는 최신 버전에서는 다음과 같은 오류 메시지를 표시합니다.

Description:
The dependencies of some of the beans in the application context form a cycle:
┌─────┐
|  jdbcAccountRepository defined in file [repositories\JdbcAccountRepository.class]
↑     ↓
|  securityConfiguration.WebSecurityConfig (field services.JdbcUserDetailsServicessecurity.SecurityConfiguration$WebSecurityConfig.userDetailsService)
↑     ↓
|  jdbcUserDetailsServices (field repositories.JdbcAccountRepository services.JdbcUserDetailsServices.repository)
└─────┘

원래 코드는 다음과 같습니다.

계정 리포지토리:

public interface AccountRepository {
    void createAccount(Account user) throws UsernameAlreadyInUseException;
    Account findAccountByUsername(String username);
}

Jdbc 계정 리포지토리:

@Repository
public class JdbcAccountRepository implements AccountRepository {

    private final Logger LOGGER = LoggerFactory.getLogger(this.getClass());

    private final JdbcTemplate jdbcTemplate;
    private final PasswordEncoder passwordEncoder;

    @Autowired
    public JdbcAccountRepository(JdbcTemplate jdbcTemplate, PasswordEncoder passwordEncoder) {
        this.jdbcTemplate = jdbcTemplate;
        this.passwordEncoder = passwordEncoder;
    }

    @Transactional
    @Override
    public void createAccount(Account user) throws UsernameAlreadyInUseException {
        try {
            jdbcTemplate.update(
                "insert into Account (firstName, lastName, username, password, role) values (?, ?, ?, ?, ?)",
                user.getFirstName(),
                user.getLastName(),
                user.getUsername(),
                passwordEncoder.encode(
                        user.getPassword()),
                        user.getRole()
            );
        } catch (DuplicateKeyException e) {
            throw new UsernameAlreadyInUseException(user.getUsername());
        }
    }

    @Override
    public Account findAccountByUsername(String username) {
        return jdbcTemplate.queryForObject(
            "select username, password, firstName, lastName, role from Account where username = ?",
            (rs, rowNum) -> new Account(
                    rs.getString("username"),
                    rs.getString("password"),
                    rs.getString("firstName"),
                    rs.getString("lastName"),
                    rs.getString("role")),
            username
        );
    }
}

Jdbc 사용자 세부 서비스:

@Service
public class JdbcUserDetailsServices implements UserDetailsService {
    private final Logger LOGGER = LoggerFactory.getLogger(this.getClass());

    @Autowired
    JdbcAccountRepository repository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        try {
            Account account = repository.findAccountByUsername(username);
            User user = new User(
                account.getUsername(),
                account.getPassword(),
                AuthorityUtils.createAuthorityList(
                        account.getRole()
                )
            );
            return user;
        } catch (DataAccessException e) {
            LOGGER.debug("Account not found", e);
            throw new UsernameNotFoundException("Username not found.");
        }
    }
}

보안 구성:

@Configuration
@ComponentScan
public class SecurityConfiguration {

    @Autowired
    ApplicationContext context;

    @Autowired
    VaadinSecurity security;

    @Bean
    public PreAuthorizeSpringViewProviderAccessDelegate preAuthorizeSpringViewProviderAccessDelegate() {
        return new PreAuthorizeSpringViewProviderAccessDelegate(security, context);
    }

    @EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
    public static class GlobalMethodSecurity extends GlobalMethodSecurityConfiguration {

        @Bean
        @Override
        protected AccessDecisionManager accessDecisionManager() {
            return super.accessDecisionManager();
        }
    }

    @Configuration
    @EnableWebSecurity
    public static class WebSecurityConfig extends WebSecurityConfigurerAdapter {

        @Autowired
        JdbcUserDetailsServices userDetailsService;

        @Autowired
        DataSource dataSource;

        @Bean
        public PasswordEncoder passwordEncoder() {
            return NoOpPasswordEncoder.getInstance();
        }

        @Bean
        public TextEncryptor textEncryptor() {
            return Encryptors.noOpText();
        }

        /*
         * (non-Javadoc)
         * @see org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
         * #configure(org.springframework.security.config.annotation.web.builders.WebSecurity)
         */
        @Override
        public void configure(WebSecurity web) throws Exception {
            //Ignoring static resources
            web.ignoring().antMatchers("/VAADIN/**");
        }

        @Override
        protected void configure(AuthenticationManagerBuilder auth)
            throws Exception {
            auth.userDetailsService(userDetailsService);
        }

        @Bean(name="authenticationManager")
        @Override
        public AuthenticationManager authenticationManagerBean() throws Exception {
            return super.authenticationManagerBean();
        }

        /*
         * (non-Javadoc)
         * @see org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
         * #configure(org.springframework.security.config.annotation.web.builders.HttpSecurity)
         */
        @Override
        protected void configure(HttpSecurity http) throws Exception {

            http
                .exceptionHandling()
                    .authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/"))
                    .and()
                .authorizeRequests()
                    .antMatchers("/**").permitAll()
                    .and()
                .csrf().disable();
        }
    }
}

추신: Spring Boot 버전을 [1.1.5, 1.2.0]으로 다운그레이드하면 이 문제가 발생하지 않습니다(다른 종속성 때문에 최신 버전을 사용해야 함).

생성자 기반 종속성 주입을 설정자 기반 종속성 주입으로 대체하여 주기를 해결할 수 있습니다. Spring Framework 참조 문서:

순환 종속성

주로 생성자 주입을 사용하는 경우 해결할 수 없는 순환 종속성 시나리오를 생성할 수 있습니다.

예를 들어, 클래스 A는 생성자 주입을 통해 클래스 B의 인스턴스를 필요로 하며 클래스 B는 생성자 주입을 통해 클래스 A의 인스턴스를 필요로 합니다.클래스 A와 B에 대해 서로 주입할 콩을 구성하면 Spring IoC 컨테이너가 런타임에 이 순환 참조를 감지하고 다음을 던집니다.BeanCurrentlyInCreationException.

한 가지 가능한 해결책은 생성자가 아닌 설정자가 구성하도록 일부 클래스의 소스 코드를 편집하는 것입니다.또는 Constructor injection을 피하고 setter injection만 사용합니다.즉, 권장하지 않지만 setter injection으로 원형 종속성을 구성할 수 있습니다.

일반적인 경우(원형 의존성 없음)와 달리, 콩 A와 콩 B 사이의 원형 의존성은 콩 중 하나가 완전히 초기화되기 전에 다른 하나에 주입되도록 강요합니다(고전적인 닭/계란 시나리오).

저는 더 좋아요.@Lazy방법.그렇게 하면 한 가지 패턴을 고수할 수 있습니다.

http://www.baeldung.com/circular-dependencies-in-spring 참조

당신의.PasswordEncoder콩의 정의는 에 있습니다.WebSecurityConfig어떤것이 필요합니까?JdbcUserDetailsServices.JdbcUserDetailsServices다시 의존합니다.JdbcAccountRepository어떤것이 필요합니까?PasswordEncoder 그래서 주기가 형성됩니다.입니다.PasswordEncoderWebSecurityConfig에서도.SecurityConfiguration수업은 순환문제를 풀것입니다.

사용했습니다.@Lazy하나의 클래스의 구성자에서 그것은 나의 문제를 해결했습니다.

public class AService {  
    private BService b;   
    public ApplicantService(@NonNull @Lazy BService b) {    
        this.b = b;  
    }
}  

public class BService {  
    private AService a;   
    public ApplicantService(@NonNull BService a) {  
        this.a = a;  
    }

}

지산의 답변:

암호 인코더 정의는 JdbcUserDetailsServices가 필요한 WebSecurityConfig에 있습니다.JdbcUserDetailsServices가 다시 PasswordEncoder가 필요한 JdbcAccountRepository에 종속되었습니다.그래서 주기가 형성됩니다.간단한 해결책은 WebSecurityConfig에서 PasswordEncoder bean 정의를 꺼내는 것입니다.SecurityConfiguration 클래스 내부에서도 순환 문제가 해결됩니다.

또 다른 간단한 제안은 PasswordEncoder 정의를 public에서 public static으로만 변경하는 것입니다.

@Bean(name = "passwordEncoder")
public PasswordEncoder passwordencoder() {
    return new CustomPasswordEncoder();
}

받는 사람:

@Bean(name = "passwordEncoder")
public static PasswordEncoder passwordencoder() {
    return new CustomPasswordEncoder();
}

@지산 아드난이 맞습니다.PasswordEncoderWebSecurityConfig는 순환 종속성 문제를 해결합니다.

PasswordEncoder에 대한 별도의 config 클래스를 만든 다음 securityconfig 클래스로 가져오면 문제가 해결됩니다.SourceCode 최신 스프링부팅이 포함된 여기 ->

해결책 중 하나는 시공자를 사용하지 않는 것입니다.예를 들어 다음 대신:

private final JdbcTemplate jdbcTemplate;
private final PasswordEncoder passwordEncoder;

@Autowired
public JdbcAccountRepository(JdbcTemplate jdbcTemplate, PasswordEncoder passwordEncoder) {
    this.jdbcTemplate = jdbcTemplate;
    this.passwordEncoder = passwordEncoder;
}

다음을 사용할 수 있습니다.

@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private PasswordEncoder passwordEncoder;

언급URL : https://stackoverflow.com/questions/40695893/spring-security-circular-bean-dependency

반응형