web-dev-qa-db-fra.com

Impossible d'utiliser Keycloak dans Spring Boot 2.1 en raison de l'enregistrement dupliqué du bean httpSessionManager

Je souhaite sécuriser mon application Spring Boot 2.1 avec Keycloak 4.5.

Actuellement, je ne peux pas démarrer l'application en raison de l'erreur suivante:

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.

Ma classe SecurityConfig (voir ci-dessous) s'étend de KeycloakWebSecurityConfigurerAdapter. Cet adaptateur définit déjà le bean httpSessionManager.

Je comprends pourquoi c'est un problème. La question est, comment puis-je empêcher cela ou résoudre mon conflit?

Les étapes que j'ai faites jusqu'à présent:

  • Construit mon pom (voir ci-dessous) en utilisant:
    • printemps-botte-starter-web
    • printemps-boot-starter-security
    • porte-clés-printemps-démarrage-démarreur
    • keycloak-adapter-bom dans dependencyManagement
  • Défini un propre SecurityConfig étendant 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();
    }
}

Update Il existe un problème connu ( KEYCLOAK-8725 ). Le correctif est prévu pour 5.x de Keycloak. Cependant, il y avait une solution de contournement dans les commentaires. Il suffit de remplacer l'annotation @KeyCloakConfiguration par:

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

Il semble y avoir un bogue dans l’intégration de Spring Security de Keycloak, ce qui signifie qu’une application qui sous-classe KeycloakWebSecurityConfigurerAdapter essaiera de créer deux beans nommés httpSessionManager. Lorsque deux beans sont définis avec le même nom, la deuxième définition rencontrée tentera de remplacer le premier. Ce remplacement est interdit par défaut dans Spring Boot 2.1. Je recommanderais de signaler ceci comme un bogue contre l'intégration de Spring Security de Keycloak. En attendant que le bogue soit résolu, vous pouvez contourner le problème en définissant spring.main.allow-bean-definition-overriding=true dans application.properties.

14
Andy Wilkinson

Cela m'a aidé à résoudre un problème, supprimez @KeycloakConfiguration et utilisez ceci à la place (de KEYCLOAK-8725 ):

Java:

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

Kotlin:

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

J'utilise keycloak-spring-security-adapter dans la version 6.0.1.La solution pour supprimer @KeycloakConfiguration avec une configuration spéciale ne fonctionnait pas pour moi.

Ma solution consistait à ajouter la ligne suivante dans application.properties:

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

Le moyen préféré pour traiter la définition du bean HttpSessionManager en double consiste à remplacer la création de ce bean dans votre SecurityConfig et à ajouter une annotation conditionnelle à son instanciation, comme suit:

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