Vous faites de la programmation réactive avec Spring webflux et vous souhaitez utiliser Keycloak pour protéger vos apis ? Je vous explique dans cet article comment effectuer cela en  quelques étapes. En effet depuis sa version 5.1 ,  spring security vient avec le module spring-security-oauth2-resource-server pour les cas d’utilisation d’IAM (Identity Access Management) comme Keycloak ou Okta …  et c’est plutôt simple …

NB: Je considère que vous utilisez déjà Keycloak et que vous savez comment configurer un domaine et y ajouter des clients. Si ce n’est pas le cas, nos articles précédents vous montrent la voie.

Fiche Technique

Keycloak : 4.8.1.Final

Spring boot : 2.1.1.RELEASE

Spring webflux : 2.1.1.RELEASE

Spring Oauth2 : 5.1.2.RELEASE

Etape 1 :

Un petit tour sur https://start.spring.io/  pour initialiser un projet avec les dépendances citées ci-dessus.

Rajoutez ensuite ces deux dépendances très importantes qui permettront à Spring Boot de configurer automatiquement une sécurité oauth2

<dependency>
  <groupId>org.springframework.security</groupId>
  <artifactId>spring-security-oauth2-resource-server</artifactId>
  <version>5.1.2.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework.security</groupId>
  <artifactId>spring-security-oauth2-jose</artifactId>
  <version>5.1.2.RELEASE</version>
</dependency>

Ces dépendances viennent évidemment accompagner spring-boot-starter-security déjà présent dans votre pom.xml.

Etape 2 :

Ouvrez votre fichier application.properties et ajouter la ligne suivante :

spring.security.oauth2.resourceserver.jwt.issuer-uri=issuer_url

issuer_url est bien sûr l’URL qui permet d’accéder à votre realm keycloak (ex: http://localhost:8080/auth/realms/demo-realm). Spring récupère cet url puis la concatène avec /.well_known/openid-configuration pour initialiser la configuration du serveur de ressources. Assurez-vous donc que l’URL est bel et bien accessible.

Vous trouverez un exemple de configuration de realm ici.

Etape 3 :

Il ne nous reste plus qu’à créer une classe de configuration et y coller le code suivant :

@EnableWebFluxSecurity

class SecurityConfig {


  
  @Bean

  SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
    // @formatter:off
    http

      .authorizeExchange()

      .anyExchange().authenticated()
 
      .and()
 
      .oauth2ResourceServer()
 
      .jwt();
    // @formatter:on
    
return http.build();
}

}

Et voilà le tour est joué !!!

L’intérêt d’utiliser cette approche (générique) est de pouvoir changer d’IAM sans pour autant changer les dépendances (c’est aussi valable pour les numéros de version).

Si vous souhaitez ajouter une gestion des rôles, le code ci-dessous pourrait vous être utile :

@Bean
  SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
    // @formatter:off
    http
        .csrf().disable()
        .httpBasic().disable()
        .formLogin().disable()
        .authorizeExchange()
          .pathMatchers(HttpMethod.OPTIONS).permitAll()
          .pathMatchers("/user").hasAuthority("USER")
          .pathMatchers("/admin").hasAuthority("ADMIN")
        .anyExchange().denyAll()
        .and()
        .oauth2ResourceServer()
          .jwt()
            .jwtAuthenticationConverter(grantedAuthoritiesExtractor());
    // @formatter:on
    return http.build();
  }

  private Converter<Jwt, Mono<AbstractAuthenticationToken>> grantedAuthoritiesExtractor() {
    GrantedAuthoritiesExtractor extractor = new GrantedAuthoritiesExtractor();
    return new ReactiveJwtAuthenticationConverterAdapter(extractor);
  }

  static class GrantedAuthoritiesExtractor extends JwtAuthenticationConverter {

    @Override
    protected Collection<GrantedAuthority> extractAuthorities(Jwt jwt) {
      Map<String, Object> claims = jwt.getClaims();
      JSONObject realmAccess = (JSONObject) claims.get("realm_access");
      JSONArray roles = (JSONArray) realmAccess.get("roles");

      return roles.stream()
          .map(Object::toString)
          .map(SimpleGrantedAuthority::new)
          .collect(Collectors.toSet());
    }
  }

Liens utiles

https://docs.spring.io/spring-security/site/docs/5.1.2.RELEASE/reference/htmlsingle/

https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html

https://github.com/spring-projects/spring-security/tree/master/samples/boot/oauth2resourceserver-webflux

Notre série d’articles Keycloak :