【问题标题】:Spring Boot 2.6 regression: How can I fix Keycloak circular dependency in adapter?Spring Boot 2.6 回归:如何修复适配器中的 Keycloak 循环依赖?
【发布时间】:2021-12-02 23:06:51
【问题描述】:

Spring Boot 2.6.x 似乎引入了一些更改,导致之前与 Keycloak 的集成具有循环引用,从而阻止了应用程序启动;它可以在当前的 2.5.x 版本中正常运行。

明确地,通过 除了 spring-boot-starter-parent 中的 标记值从 2.5.7 更改为 2.6.1 之外什么都不更改,错误/消息下文详述。

当然,预期的行为是应用程序可以正常启动,并且像以前一样使用 Keycloak 进行保护。

实际消息是:

***************************
APPLICATION FAILED TO START
***************************

Description:

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

┌──->──┐
|  keycloakSecurityConfig (field private org.keycloak.adapters.KeycloakConfigResolver org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter.keycloakConfigResolver)
└──<-──┘

完整的堆栈跟踪:

2021-11-30 12:49:07.308 DEBUG 7 --- [           main] o.s.b.d.LoggingFailureAnalysisReporter   : Application failed to start due to an exception

org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'keycloakSecurityConfig': Requested bean is currently in creation: Is there an unresolvable circular reference?        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.beforeSingletonCreation(DefaultSingletonBeanRegistry.java:355) ~[spring-beans-5.3.13.jar!/:5.3.13]
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:227) ~[spring-beans-5.3.13.jar!/:5.3.13]
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.13.jar!/:5.3.13]
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.13.jar!/:5.3.13]
        at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:410) ~[spring-beans-5.3.13.jar!/:5.3.13]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1352) ~[spring-beans-5.3.13.jar!/:5.3.13]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1195) ~[spring-beans-5.3.13.jar!/:5.3.13]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:582) ~[spring-beans-5.3.13.jar!/:5.3.13]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) ~[spring-beans-5.3.13.jar!/:5.3.13]
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.13.jar!/:5.3.13]
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.13.jar!/:5.3.13]
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.13.jar!/:5.3.13]
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.13.jar!/:5.3.13]
        at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276) ~[spring-beans-5.3.13.jar!/:5.3.13]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1380) ~[spring-beans-5.3.13.jar!/:5.3.13]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1300) ~[spring-beans-5.3.13.jar!/:5.3.13]
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:656) ~[spring-beans-5.3.13.jar!/:5.3.13]
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:639) ~[spring-beans-5.3.13.jar!/:5.3.13]
        at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119) ~[spring-beans-5.3.13.jar!/:5.3.13]
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:399) ~[spring-beans-5.3.13.jar!/:5.3.13]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1431) ~[spring-beans-5.3.13.jar!/:5.3.13]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:619) ~[spring-beans-5.3.13.jar!/:5.3.13]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) ~[spring-beans-5.3.13.jar!/:5.3.13]
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.13.jar!/:5.3.13]
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.13.jar!/:5.3.13]
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.13.jar!/:5.3.13]
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.13.jar!/:5.3.13]
        at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:410) ~[spring-beans-5.3.13.jar!/:5.3.13]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1352) ~[spring-beans-5.3.13.jar!/:5.3.13]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1195) ~[spring-beans-5.3.13.jar!/:5.3.13]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:582) ~[spring-beans-5.3.13.jar!/:5.3.13]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) ~[spring-beans-5.3.13.jar!/:5.3.13]
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.13.jar!/:5.3.13]
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.13.jar!/:5.3.13]
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.13.jar!/:5.3.13]
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:213) ~[spring-beans-5.3.13.jar!/:5.3.13]
        at org.springframework.boot.web.servlet.ServletContextInitializerBeans.getOrderedBeansOfType(ServletContextInitializerBeans.java:212) ~[spring-boot-2.6.1.jar!/:2.6.1]
        at org.springframework.boot.web.servlet.ServletContextInitializerBeans.addAsRegistrationBean(ServletContextInitializerBeans.java:175) ~[spring-boot-2.6.1.jar!/:2.6.1]
        at org.springframework.boot.web.servlet.ServletContextInitializerBeans.addAsRegistrationBean(ServletContextInitializerBeans.java:170) ~[spring-boot-2.6.1.jar!/:2.6.1]
        at org.springframework.boot.web.servlet.ServletContextInitializerBeans.addAdaptableBeans(ServletContextInitializerBeans.java:155) ~[spring-boot-2.6.1.jar!/:2.6.1]
        at org.springframework.boot.web.servlet.ServletContextInitializerBeans.<init>(ServletContextInitializerBeans.java:87) ~[spring-boot-2.6.1.jar!/:2.6.1]
        at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.getServletContextInitializerBeans(ServletWebServerApplicationContext.java:260) ~[spring-boot-2.6.1.jar!/:2.6.1]
        at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.selfInitialize(ServletWebServerApplicationContext.java:234) ~[spring-boot-2.6.1.jar!/:2.6.1]
        at org.springframework.boot.web.embedded.tomcat.TomcatStarter.onStartup(TomcatStarter.java:53) ~[spring-boot-2.6.1.jar!/:2.6.1]
        at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5219) ~[tomcat-embed-core-9.0.55.jar!/:?]
        at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183) ~[tomcat-embed-core-9.0.55.jar!/:?]
        at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1396) ~[tomcat-embed-core-9.0.55.jar!/:?]
        at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1386) ~[tomcat-embed-core-9.0.55.jar!/:?]
        at java.util.concurrent.FutureTask.run(FutureTask.java:264) ~[?:?]
        at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75) ~[tomcat-embed-core-9.0.55.jar!/:?]
        at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:140) ~[?:?]
        at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:919) ~[tomcat-embed-core-9.0.55.jar!/:?]
        at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:835) ~[tomcat-embed-core-9.0.55.jar!/:?]
        at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183) ~[tomcat-embed-core-9.0.55.jar!/:?]
        at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1396) ~[tomcat-embed-core-9.0.55.jar!/:?]
        at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1386) ~[tomcat-embed-core-9.0.55.jar!/:?]
        at java.util.concurrent.FutureTask.run(FutureTask.java:264) ~[?:?]
        at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75) ~[tomcat-embed-core-9.0.55.jar!/:?]
        at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:140) ~[?:?]
        at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:919) ~[tomcat-embed-core-9.0.55.jar!/:?]
        at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:263) ~[tomcat-embed-core-9.0.55.jar!/:?]
        at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183) ~[tomcat-embed-core-9.0.55.jar!/:?]
        at org.apache.catalina.core.StandardService.startInternal(StandardService.java:432) ~[tomcat-embed-core-9.0.55.jar!/:?]
        at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183) ~[tomcat-embed-core-9.0.55.jar!/:?]
        at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:927) ~[tomcat-embed-core-9.0.55.jar!/:?]
        at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183) ~[tomcat-embed-core-9.0.55.jar!/:?]
        at org.apache.catalina.startup.Tomcat.start(Tomcat.java:486) ~[tomcat-embed-core-9.0.55.jar!/:?]
        at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.initialize(TomcatWebServer.java:123) ~[spring-boot-2.6.1.jar!/:2.6.1]
        at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.<init>(TomcatWebServer.java:104) ~[spring-boot-2.6.1.jar!/:2.6.1]
        at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getTomcatWebServer(TomcatServletWebServerFactory.java:473) ~[spring-boot-2.6.1.jar!/:2.6.1]
        at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getWebServer(TomcatServletWebServerFactory.java:206) ~[spring-boot-2.6.1.jar!/:2.6.1]
        at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.createWebServer(ServletWebServerApplicationContext.java:182) ~[spring-boot-2.6.1.jar!/:2.6.1]
        at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:160) ~[spring-boot-2.6.1.jar!/:2.6.1]
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:577) ~[spring-context-5.3.13.jar!/:5.3.13]
        at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:145) ~[spring-boot-2.6.1.jar!/:2.6.1]
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:730) [spring-boot-2.6.1.jar!/:2.6.1]
        at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:412) [spring-boot-2.6.1.jar!/:2.6.1]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:302) [spring-boot-2.6.1.jar!/:2.6.1]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1301) [spring-boot-2.6.1.jar!/:2.6.1]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1290) [spring-boot-2.6.1.jar!/:2.6.1]
        at REDACTED.SpringPortalApplication.main(SpringPortalApplication.java:17) [classes!/:2.0.0]
        at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]
        at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:?]
        at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
        at java.lang.reflect.Method.invoke(Method.java:566) ~[?:?]
        at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49) [spring-portal.jar:2.0.0]
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:108) [spring-portal.jar:2.0.0]
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:58) [spring-portal.jar:2.0.0]
        at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:88) [spring-portal.jar:2.0.0]

2021-11-30 12:49:07.315 ERROR 7 --- [           main] o.s.b.d.LoggingFailureAnalysisReporter   :

***************************
APPLICATION FAILED TO START
***************************

Description:

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

┌──->──┐
|  keycloakSecurityConfig (field private org.keycloak.adapters.KeycloakConfigResolver org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter.keycloakConfigResolver)
└──<-──┘


Action:

Relying upon circular references is discouraged and they are prohibited by default. Update your application to remove the dependency cycle between beans. As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true.

POM 非常广泛,因为它包含许多 spring-boot-starter-... (web,security) 条目,以及其他无趣的项目依赖项。我认为可能与这个特定问题相关的是 Keycloak 相关的事情:

中:

  • org.keycloak.bom:keycloak-adapter-bom:15.0.2(通过 POM 变量固定的版本)

作为 条目(来自 spring-boot-starter-parent 或 Keycloak BOM 的预期版本):

  • org.springframework.boot:spring-boot-starter-security
  • org.keycloak:keycloak-spring-boot-starter

存在的唯一相关配置类是(为简洁起见,删除了 Javadoc):

@Configuration
@EnableWebSecurity
@ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
public class KeycloakSecurityConfig extends KeycloakWebSecurityConfigurerAdapter {

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) {
        KeycloakAuthenticationProvider kap = keycloakAuthenticationProvider();
        kap.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
        auth.authenticationProvider(kap);
    }

    @Bean
    public KeycloakSpringBootConfigResolver keycloakConfigResolver() {
        return new KeycloakSpringBootConfigResolver();
    }

    @Bean
    @Override
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
        http.authorizeRequests()
                .antMatchers("/**")
                .authenticated();
    }

}

我稍微戳了一下 Keycloak 代码,我为上述配置类扩展的抽象 o.k.a.s.c.KeycloakWebSecurityConfigurerAdapter 似乎有一个 @Autowired(required = false) 类型为 o.k.a.KeycloakConfigResolver 的私有字段 - 正是循环引用错误中提到的内容。

我试过了:

  • 将带注释的@Bean 提供者方法移动到另一个类;导致在循环引用错误中也提到了该类,作为类路径上的一个项目。

  • 只需删除返回 KeycloakSpringBootConfigResolver 的提供程序方法,并查看是否有其他东西提供它;我意识到这没有意义,并且它按预期失败,因为这里使用的 Keycloak 资源没有任何变化(从 15.0.2 开始) - 此外,如果字段为 null,则获取配置的默认方法是 JSON 文件在o.k.a.s.c.KeycloakWebSecurityConfigurerAdapter::adapterDeploymentContext

  • 阅读更多,现在对我来说不再有意义为什么我实际工作的东西 - 它有一个构造函数注入的 o.k.a.KeycloakConfigResolver 实例,它必须使用的提供程序是一个非静态方法 本身 - 这意味着必须构建一个实例。根据前面的要点,注释掉我提供 o.k.a.KeycloakConfigResolver 实现的方法会导致它默认为尝试 JSON 文件的注入字段为空条件。

可能相关,但不确定; 似乎不是:最近的问题Spring boot keycloak circular reference

【问题讨论】:

  • 对于spring-boot developers (github.com),这听起来像是一个完美的问题(也可能是一个错误报告)。
  • 您的问题与 Prohibit circular references by default #27652 有关,我不确定默认情况下禁止循环依赖的动机是什么,但现在一般建议是“您的代码很差 - 重构它”,另一方面您可以通过“spring.main.allow-circular-references=true”禁用此功能或继续调查(您是否尝试过将@Lazy 放在 keycloakConfigResolver() 上?)
  • keycloakConfigResolver 的 bean 创建移动到另一个文件暂时解决了这个问题。
  • Keycloak 团队弃用了 Spring 适配器。如您所见/检查,有 Keycloak 15.1.0,但没有 org.keycloak:keycloak-spring-boot-starter:15.1.0。最后一个是 15.0.2 keycloak.org/2021/12/keycloak-1510-released.html。然后,可能我们需要更换 Keycloak 适配器,而不是期待 Keycloak 团队的解决方案。

标签: java spring spring-boot spring-security keycloak


【解决方案1】:

允许循环依赖可能不是最好的选择。

您可以做的一件事是为您的 KeycloakConfigResolver bean 创建一个特定的配置类。

package com.stackoverflow;

import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class KeycloakConfiguration {

    @Bean
    public KeycloakSpringBootConfigResolver KeycloakConfigResolver() {
        return new KeycloakSpringBootConfigResolver();
    }
}

【讨论】:

  • 这对我有用... Spring Boot 2.6.4 和 Keycloak 14.0.0
  • 这对我有用 keycloak 17 spring boot 2.6.4
猜你喜欢
  • 2022-01-18
  • 2020-07-15
  • 2018-11-01
  • 2015-05-17
  • 1970-01-01
  • 1970-01-01
  • 2020-09-25
  • 1970-01-01
  • 2018-07-25
相关资源
最近更新 更多