Невозможно использовать Keycloak в Spring Boot 2.1 из-за дублированной регистрации компонента httpSessionManager

Я хочу защитить свое приложение Spring Boot 2.1 с помощью Keycloak 4.5.

В настоящее время я не могу запустить приложение из-за следующей ошибки:

Exception encountered during context initialization - cancelling refresh attempt: 
org.springframework.beans.factory.support.BeanDefinitionOverrideException: 
  Invalid bean definition with name 'httpSessionManager' defined in class path resource [dummy/service/SecurityConfig.class]: 
    Cannot register bean definition [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=securityConfig; factoryMethodName=httpSessionManager; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [dummy/SecurityConfig.class]] for bean 'httpSessionManager': 
There is already [Generic bean: class [org.keycloak.adapters.springsecurity.management.HttpSessionManager]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in URL [jar:file:/.m2/repository/org/keycloak/keycloak-spring-security-adapter/4.5.0.Final/keycloak-spring-security-adapter-4.5.0.Final.jar!/org/keycloak/adapters/springsecurity/management/HttpSessionManager.class]] bound.

Мой класс SecurityConfig (см. ниже) происходит от KeycloakWebSecurityConfigurerAdapter. Этот адаптер уже определяет bean-компонент httpSessionManager.

Я понимаю, почему это проблема. Вопрос в том, как я могу предотвратить это или исправить мой конфликт?

Шаги, которые я сделал до сих пор:

  • Построил мой пом (см. ниже), используя:
    • spring-загрузка стартер веб
    • spring-загрузки-стартер-безопасностиkeycloak-spring-загрузки-стартер
    • keycloak-adapter-bom в зависимости от управления
  • Определил собственный SecurityConfig, расширяющий KeycloakWebSecurityConfigurerAdapter

pom.xml

...
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.0.RELEASE</version>
</parent>

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

    <java.version>11</java.version>
    <maven.compiler.source>${java.version}</maven.compiler.source>
    <maven.compiler.target>${java.version}</maven.compiler.target>

    <keycloak.version>4.5.0.Final</keycloak.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
        <groupId>org.keycloak</groupId>
        <artifactId>keycloak-spring-boot-starter</artifactId>
    </dependency>
</dependencies>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.keycloak.bom</groupId>
            <artifactId>keycloak-adapter-bom</artifactId>
            <version>${keycloak.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
...

SecurityConfig.java

@KeycloakConfiguration
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Import(KeycloakWebSecurityConfigurerAdapter.class)
class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {

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

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

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

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
        http.csrf().ignoringAntMatchers("/**/*");
        http.authorizeRequests()
                .anyRequest().permitAll();
    }
}

Обновление Известная проблема (KEYCLOAK-8725). Исправление планируется для 5.x Keycloak. Однако в комментариях был обходной путь. Просто замените аннотацию @KeyCloakConfiguration на:

@Configuration
@ComponentScan(
    basePackageClasses = KeycloakSecurityComponents.class,
    excludeFilters = @ComponentScan.Filter(type = FilterType.REGEX, pattern = "org.keycloak.adapters.springsecurity.management.HttpSessionManager"))
@EnableWebSecurity

Ответы

Ответ 1

Похоже, что есть ошибка в интеграции Keycloak Spring Security, которая означает, что приложение, которое подклассов KeycloakWebSecurityConfigurerAdapter, попытается создать два bean-компонента с именем httpSessionManager. Когда два bean-компонента определены с одним и тем же именем, второе обнаруженное определение попытается переопределить первое. Это переопределение запрещено по умолчанию в Spring Boot 2.1. Я бы порекомендовал сообщить об этом как об ошибке в интеграции Keycloak Spring Security. Пока вы ждете устранения ошибки, вы можете обойти эту проблему, установив spring.main.allow-bean-definition-overriding=true в application.properties.

Ответ 2

Это помогло мне решить проблему, удалить @KeycloakConfiguration и использовать вместо этого (из KEYCLOAK-8725):

Java:

@Configuration
@ComponentScan(
        basePackageClasses = KeycloakSecurityComponents.class,
        excludeFilters = @ComponentScan.Filter(type = FilterType.REGEX, pattern = "org.keycloak.adapters.springsecurity.management.HttpSessionManager"))
@EnableWebSecurity

Котлин:

@Configuration
@ComponentScan(
    basePackageClasses = [KeycloakSecurityComponents::class],
    excludeFilters = [ComponentScan.Filter(type = FilterType.REGEX, pattern = ["org.keycloak.adapters.springsecurity.management.HttpSessionManager"])]
)
@EnableWebSecurity

Ответ 3

Я использую keycloak-spring-security-adapter в версии 6.0.1. Решение удалить @KeycloakConfiguration со специальной конфигурацией не работает для меня.

Моим решением было добавить следующую строку в application.properties:

spring.main.allow-bean-definition-overriding: true

Ответ 4

Можете ли вы показать мне свой полный класс конфигурации безопасности? Это не работает для меня спасибо

Ответ 5

Предпочтительный способ обращения к дублирующему определению бина HttpSessionManager - переопределить создание этого бина в вашем SecurityConfig и добавить условную аннотацию для его экземпляра, как показано ниже:

@KeycloakConfiguration
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Import(KeycloakWebSecurityConfigurerAdapter.class)
class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
    @Bean
    @Override
    @ConditionalOnMissingBean(HttpSessionManager.class)
    protected HttpSessionManager httpSessionManager() {
        return new HttpSessionManager();
    }
}