You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

升级Java21与Spring3.0后SecurityConfig循环依赖启动报错

问题

将项目升级至Java 21和Spring 3.0,同时把安全配置更新为非弃用格式后,启动失败,报错提示创建名为securityConfig的Bean时存在无法解析的循环依赖。已尝试旧的弃用配置方式及新的filterChain方式,且已将所有javax依赖替换为jakarta依赖。

报错堆栈信息

ERROR | SpringApplication - Application run failed org.springframework.context.ApplicationContextException: Unable to start web server         at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:164)         at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:602)         at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146)         at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:737)         at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439)         at org.springframework.boot.SpringApplication.run(SpringApplication.java:315)         at org.springframework.boot.SpringApplication.run(SpringApplication.java:1309)         at org.springframework.boot.SpringApplication.run(SpringApplication.java:1298)         at io.inowise.inox.InoxApplication.main(InoxApplication.java:12)         at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)         at java.base/java.lang.reflect.Method.invoke(Method.java:580)         at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49)         at org.springframework.boot.loader.Launcher.launch(Launcher.java:95)         at org.springframework.boot.loader.Launcher.launch(Launcher.java:58)         at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:65) Caused by: org.springframework.boot.web.server.WebServerException: Unable to start embedded Tomcat         at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.initialize(TomcatWebServer.java:142)         at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.<init>(TomcatWebServer.java:104)         at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getTomcatWebServer(TomcatServletWebServerFactory.java:489)         at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getWebServer(TomcatServletWebServerFactory.java:211)         at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.createWebServer(ServletWebServerApplicationContext.java:183)         at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:161)         ... 14 common frames omitted Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'securityConfig': Requested bean is currently in creation: Is there an unresolvable circular reference?         at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.beforeSingletonCreation(DefaultSingletonBeanRegistry.java:355)         at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:227)         at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323)         at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)         at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:412)         at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1332)         at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1162)         at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:560)         at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:520)         at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325)         at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)         at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323)         at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)         at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.resolveBeanReference(ConfigurationClassEnhancer.java:362)         at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:334)         at io.inowise.inox.security.SecurityConfig$$SpringCGLIB$$0.keycloakAuthenticationProvider(<generated>)         at io.inowise.inox.security.SecurityConfig.configureGlobal(SecurityConfig.java:115)         at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)         at java.base/java.lang.reflect.Method.invoke(Method.java:580)         at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:833)         at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:145)         at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:492)         at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1416)         at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:597)         at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:520)         at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325)         at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)         at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323)         at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)         at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:412)         at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1332)         at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1162)         at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:560)         at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:520)         at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325)         at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)         at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323)         at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:204)         at org.springframework.boot.web.servlet.ServletContextInitializerBeans.getOrderedBeansOfType(ServletContextInitializerBeans.java:210)         at org.springframework.boot.web.servlet.ServletContextInitializerBeans.getOrderedBeansOfType(ServletContextInitializerBeans.java:201)         at org.springframework.boot.web.servlet.ServletContextInitializerBeans.addServletContextInitializerBeans(ServletContextInitializerBeans.java:96)         at org.springframework.boot.web.servlet.ServletContextInitializerBeans.<init>(ServletContextInitializerBeans.java:85)         at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.getServletContextInitializerBeans(ServletWebServerApplicationContext.java:261)         at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.selfInitialize(ServletWebServerApplicationContext.java:235)         at org.springframework.boot.web.embedded.tomcat.TomcatStarter.onStartup(TomcatStarter.java:52)         at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:4850)         at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:171)         at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1332)         at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1322)         at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)         at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75)         at java.base/java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:145)         at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:866)         at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:846)         at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:171)         at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1332)         at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1322)         at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)         at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75)         at java.base/java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:145)         at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:866)         at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:241)         at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:171)         at org.apache.catalina.core.StandardService.startInternal(StandardService.java:433)         at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:171)         at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:918)         at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:171)         at org.apache.catalina.startup.Tomcat.start(Tomcat.java:485)         at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.initialize(TomcatWebServer.java:123)         ... 19 common frames omitted

原SecurityConfig配置代码

@Configuration
@EnableWebSecurity
@ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
public class SecurityConfig {

    @Value("${cors-allow-origin.url}")
    private String corsAllowOriginUrl;

    @Autowired
    private PermissionService permissionService;

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authenticationManager(authenticationManager(http))
            .httpBasic().disable()
            .csrf().disable()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            .authorizeHttpRequests(auth -> {
                auth
                    .requestMatchers(HttpMethod.GET, "/swagger-ui/**", "/v3/api-docs/**").permitAll()
                    //Other url's
            });
        return http.build();
    }

    private String[] getRoles(PermissionType permissionType) {
        return permissionService.getRoles(permissionType);
    }

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList(corsAllowOriginUrl.split(",")));
        configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE"));
        configuration.setAllowedHeaders(Arrays.asList("*"));
        configuration.setAllowCredentials(true);
        configuration.setMaxAge(3600L);
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }

    @Bean
    public SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        return new NullAuthenticatedSessionStrategy();
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(keycloakAuthenticationProvider());
    }

    @Bean
    public KeycloakAuthenticationProvider keycloakAuthenticationProvider() {
        KeycloakAuthenticationProvider keycloakAuthenticationProvider = new KeycloakAuthenticationProvider();
        keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
        return keycloakAuthenticationProvider;
    }

    @Bean
    public FilterRegistrationBean<KeycloakAuthenticationProcessingFilter> keycloakAuthenticationProcessingFilterRegistrationBean(KeycloakAuthenticationProcessingFilter filter) {
        FilterRegistrationBean<KeycloakAuthenticationProcessingFilter> registrationBean = new FilterRegistrationBean<>(filter);
        registrationBean.setEnabled(false);
        return registrationBean;
    }

    @Bean
    public FilterRegistrationBean<KeycloakPreAuthActionsFilter> keycloakPreAuthActionsFilterRegistrationBean(KeycloakPreAuthActionsFilter filter) {
        FilterRegistrationBean<KeycloakPreAuthActionsFilter> registrationBean = new FilterRegistrationBean<>(filter);
        registrationBean.setEnabled(false);
        return registrationBean;
    }

    @Bean
    public FilterRegistrationBean<KeycloakAuthenticatedActionsFilter> keycloakAuthenticatedActionsFilterBean(KeycloakAuthenticatedActionsFilter filter) {
        FilterRegistrationBean<KeycloakAuthenticatedActionsFilter> registrationBean = new FilterRegistrationBean<>(filter);
        registrationBean.setEnabled(false);
        return registrationBean;
    }

    @Bean
    public FilterRegistrationBean<KeycloakSecurityContextRequestFilter> keycloakSecurityContextRequestFilterBean(KeycloakSecurityContextRequestFilter filter) {
        FilterRegistrationBean<KeycloakSecurityContextRequestFilter> registrationBean = new FilterRegistrationBean<>(filter);
        registrationBean.setEnabled(false);
        return registrationBean;
    }

    @Bean
    @ConditionalOnMissingBean(HttpSessionManager.class)
    protected HttpSessionManager httpSessionManager() {
        return new HttpSessionManager();
    }

    @Bean
    @ConditionalOnMissingBean(HeaderBasedConfigResolver.class)
    public org.keycloak.adapters.KeycloakConfigResolver keycloakConfigResolver() {
        return new HeaderBasedConfigResolver();
    }

    @Bean
    public KeycloakRestTemplate keycloakRestTemplate(KeycloakClientRequestFactory keycloakClientRequestFactory) {
        return new KeycloakRestTemplate(keycloakClientRequestFactory);
    }

    @Bean
    public AuthenticationManager authenticationManager(HttpSecurity http) throws Exception {
        return http.getSharedObject(AuthenticationManagerBuilder.class).build();
    }
}
解决方案

问题根源

循环依赖产生的核心原因:

  • configureGlobal方法被@Autowired修饰,Spring在创建SecurityConfig Bean的过程中会调用该方法,而方法内直接调用keycloakAuthenticationProvider()(这是一个@Bean方法),此时SecurityConfig本身还在创建中,导致Spring尝试获取的Bean依赖于正在创建的Bean,形成循环。
  • 手动创建AuthenticationManager的方式不当,进一步加剧了依赖冲突。
  • 旧的Keycloak过滤器注册方式与Spring Security 3.x的新配置模式不兼容。

修复步骤

  1. 移除configureGlobal方法,改为在SecurityFilterChain中直接配置KeycloakAuthenticationProvider。
  2. 调整AuthenticationManager的获取方式:不需要手动定义AuthenticationManager Bean,而是通过HttpSecurity的authenticationProvider方法注入认证提供者,Spring会自动管理AuthenticationManager的创建。
  3. 移除禁用Keycloak过滤器的FilterRegistrationBean:在Spring Security 3.x中,Keycloak的过滤器应该通过SecurityFilterChain集成,而非注册后禁用。
  4. 确保Keycloak依赖版本适配Spring Boot 3.x:使用org.keycloak:keycloak-spring-boot-starter版本21及以上,该版本已适配jakarta命名空间。

修改后的SecurityConfig代码示例

@Configuration
@EnableWebSecurity
@ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
public class SecurityConfig {

    @Value("${cors-allow-origin.url}")
    private String corsAllowOriginUrl;

    @Autowired
    private PermissionService permissionService;

    @Autowired
    private KeycloakAuthenticationProvider keycloakAuthenticationProvider;

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authenticationProvider(keycloakAuthenticationProvider)
            .httpBasic().disable()
            .csrf().disable()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            .authorizeHttpRequests(auth -> {
                auth
                    .requestMatchers(HttpMethod.GET, "/swagger-ui/**", "/v3/api-docs/**").permitAll()
                    // 其他URL权限配置
            });
        return http.build();
    }

    private String[] getRoles(PermissionType permissionType) {
        return permissionService.getRoles(permissionType);
    }

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList(corsAllowOriginUrl.split(",")));
        configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE"));
        configuration.setAllowedHeaders(Arrays.asList("*"));
        configuration.setAllowCredentials(true);
        configuration.setMaxAge(3600L);
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 00:13:26