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