shiroの知恵袋

元SEのIT講師shiroが、プログラミングや趣味を語るブログです。

【バグ記録】Spring Securityの設定クラスで循環参照エラーが発生する場合の対処方法

現象

Spring Boot 2.6にアップデートした際、アプリ起動時にSecurityConfigで下記のエラーが発生する。

Description: The dependencies of some of the beans in the application context form a cycle:

┌──->──┐

| securityConfig

└──<-──┘

原因

SecurityConfigにおいて、configureGlobalメソッドで使用する自身のBeanを@Autowiredしようとしていることが原因で循環参照が発生している。

※Spring Boot 2.6以上では循環参照がデフォルトで禁止されている。

(SecurityConfig.java

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 省略
    }

    @Autowired    // !!ここが原因!!
    public void configureGlobal(
            AuthenticationManagerBuilder auth,
            UserDetailsServiceImpl userDetailsService,
            PasswordEncoder passwordEncoder) throws Exception {
        auth.eraseCredentials(true).userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
    }

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

}

解決方法

① configureGlobalを別クラスにする

(SecurityConfig.java

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 省略
    }

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

}

(GlobalConfiguration.java

@Configuration
@EnableWebSecurity
public class GlobalConfiguration {
    @Autowired
    public void configureGlobal(
            AuthenticationManagerBuilder auth,
            UserDetailsServiceImpl userDetailsService,
            PasswordEncoder passwordEncoder) throws Exception {
        auth.eraseCredentials(true).userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
    }
}

② @Lazyアノテーションを付与する

@Lazyアノテーションで遅延初期化をさせます。 @Lazyが付与された@Bean または @Component は、別の Bean によって参照されるか、それを囲む BeanFactory から明示的に取得されるまで初期化されません。

リファレンス: Lazy (Spring Framework API) - Javadoc

(SecurityConfig.java

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 省略
    }

    @Autowired
    @Lazy  // 追加
    public void configureGlobal(
            AuthenticationManagerBuilder auth,
            UserDetailsServiceImpl userDetailsService,
            PasswordEncoder passwordEncoder) throws Exception {
        auth.eraseCredentials(true).userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
    }

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

}