it-source

스프링 부트 2.6 회귀:어댑터의 Keyclock 순환 의존성을 수정하려면 어떻게 해야 합니까?

criticalcode 2023. 2. 16. 21:47
반응형

스프링 부트 2.6 회귀:어댑터의 Keyclock 순환 의존성을 수정하려면 어떻게 해야 합니까?

Spring Boot 2.6.x 에서는, 몇개의 변경이 가해져, 이전에 동작하고 있던 Keycloak 와의 통합이 순환 참조가 되어, 애플리케이션의 기동을 방해하고 있습니다.현재의 2.5.x 릴리스에서는 정상적으로 동작해, 기동합니다.

명시적으로 spring-boot-starter-parent에서 <version>태그 값을 2.5.7에서2.6.1로 변경하지 않으면 다음에 설명된 오류/메시지가 발생합니다.

물론 예상 동작은 애플리케이션이 정상적으로 시작되고 이전과 마찬가지로 Keyclock으로 보호된다는 것입니다.

실제 메시지는 다음과 같습니다.

***************************
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은 스프링 부츠 스타터를 많이 포함하고 있다는 점에서 광범위합니다.(web, security) 엔트리와 기타 관심 없는 프로젝트 의존관계.이 문제와 관련이 있을 것으로 생각되는 것은 Keyclock 관련 사항은 다음과 같습니다.

<dependency Management>에서:

  • org.keycloak 입니다.bom: keycloak-adapter-bom: 15.0.2(POM 변수를 통해 핀 접속된 버전)

또한 <dependency> 엔트리(spring-boot-starter-parent 또는 Keyclock BOM에서 예상되는 버전)로서 다음과 같습니다.

  • org. springframework.부트: spring-boot-security
  • org.keycloak:keycloak-spring-boot-syslog

관련된 컨피규레이션클래스는 다음과 같습니다(간단하게 하기 위해 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 코드를 살짝 찔러봤는데 추상적인 같아요.위의 설정 클래스에 대해 확장한 KeycloakWebSecurityConfigrAdapter에는 ok.a 유형의 @Autowired(필수 = false) 개인 필드가 있습니다.Keycloak Config Resolver - 순환 참조 오류에 기재된 내용과 동일합니다.

시도했습니다.

  • @Bean 주석이 달린 프로바이더메서드를 이동하면 KeycloakConfigResolver(위의 설정 클래스에서는 KeycloakSpringBootConfigResolver)의 구현이 다른 클래스로 반환됩니다.그 결과 해당 클래스는 순환 참조 오류에서도 클래스 경로의 항목으로 언급됩니다.

  • KeycloakSpringBootConfigResolver를 반환하는 프로바이더 메서드를 삭제하고 다른 메서드가 있는지 확인했습니다.이것은 의미가 없다는 것을 깨달았습니다.또, 여기서 사용되고 있는 Keycloak 리소스에는 아무것도 변경되지 않았기 때문에(15.0.2 이후) 기대했던 대로 실패합니다.또, 필드가 null인 경우 설정을 취득하는 기본 메서드입니다.JSON 파일입니다.Keycloak Web Security Configurer Adapter::adapter Deployment Context.

  • 더 읽어보면, 이제 내가 실제로 가지고 있는 것이 왜 효과가 있는지 더 이상 이해할 수 없습니다. - 건설자가 주입한 OK.k.a. 인스턴스가 있습니다.KeycloakConfigResolver와 그 프로바이더는 그 자체로 비정적 메서드입니다.이는 인스턴스가 구축되어야 함을 의미합니다.앞의 요점에 따르면, OK.a.의 실행을 제공하는 내 방법에 대해 코멘트한다.KeycloakConfigResolver는 기본적으로 JSON 파일의 injected-field-is-null 상태가 됩니다.

관련이 있을 수 있지만 확실하지는 않습니다.다음은 스프링 부트 키 잠금 순환 참조에 대한 매우 최근의 질문입니다.

순환 종속성을 허용하는 것이 가장 좋은 옵션이 아닐 수 있습니다.

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();
    }
}

2022년 갱신

스프링에는 키 잠금 어댑터를 사용하지 마십시오. 매우 권장되지 않습니다.

대신 사용(또는spring-boot-starter-oauth2-clientTymeleaf 은를를UI 공를를를를를)

서버 API은 다음과 할 수 ( 래퍼 ).spring-boot-starter-oauth2-resource-server

<dependency>
    <groupId>com.c4-soft.springaddons</groupId>
    <!-- replace "webmvc" with "weblux" if your app is reactive -->
    <!-- replace "jwt" with "introspecting" to use token introspection instead of JWT decoding -->
    <artifactId>spring-addons-webmvc-jwt-resource-server</artifactId>
    <!-- this version is to be used with spring-boot 3.0.0-RC2, use 5.x for spring-boot 2.6.x or before -->
    <version>6.0.6</version>
</dependency>
@EnableMethodSecurity
public static class WebSecurityConfig { }
com.c4-soft.springaddons.security.issuers[0].location=https://localhost:8443/realms/realm1
com.c4-soft.springaddons.security.issuers[0].authorities.claims=realm_access.roles,ressource_access.some-client.roles,ressource_access.other-client.roles


com.c4-soft.springaddons.security.cors[0].path=/some-api

위 샘플의 5.xlibs는 부트 2.6 이전에서 동작합니다.github repo 5.x 브랜치에도 키코크 스프링 어댑터가 있는 작업 샘플이 있습니다(단, 사용하지 않는 것이 좋습니다).

언급URL : https://stackoverflow.com/questions/70207564/spring-boot-2-6-regression-how-can-i-fix-keycloak-circular-dependency-in-adapte

반응형