La gestion des accès basée sur les rôles est essentielle pour toute application qui traite avec des utilisateurs pouvant accéder à des ressources en fonction de leur rôle dans une organisation.
Dans un blog précédent, on a appris comment sécuriser notre API REST Spring Boot avec Keycloak en utilisant le protocole d'authentification OpenID Connect.
Dans ce blog, on va développer cette application d'exemple et ajouter une autorisation basée sur les rôles.
L'objectif est d'autoriser l'accès à certains endpoints uniquement aux utilisateurs ayant un rôle spécifique. Plus précisément, on va restreindre l'accès au point de terminaison DELETE aux seuls utilisateurs ayant le rôle admin.
Ajout d'un nouveau endpoint
On commence par étendre notre contrôleur Spring en ajoutant un nouveau point de terminaison DELETE.
package com.mozen.springbootkeycloack.controller;
import com.mozen.springbootkeycloack.model.Plant;
import com.mozen.springbootkeycloack.service.PlantService;
import com.sun.istack.NotNull;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
@Slf4j
@RestController()
@RequestMapping("/plant")
public class PlantController {
...
@DeleteMapping("/{plantId}")
public void deletePlant(@PathVariable long plantId) {
log.info("Delete plant request for plant " + plantId + " received");
plantService.deletePlant(plantId);
}
}
Extension de la configuration
On revient au fichier application.yml et on spécifie une nouvelle propriété pour la configuration Keycloak.
...
keycloak:
...
use-resource-role-mappings: false
On fixe la propriété use-resource-role-mappings
sur false.
Cela signifie que l'autorisation sera basée sur les rôles du niveau du realm Keycloak et non sur les rôles spécifiques au client de l'application Spring Boot.
On fait cela car le rôle d'administrateur Keycloak par défaut qu'on utilise est défini comme un rôle de niveau royaume.
Cette propriété est déjà réglée sur false par défaut, mais je trouve que la définir explicitement la rend plus claire dans ce contexte. Cependant, c'est une question de préférence, vous pouvez la laisser de côté si vous le souhaitez.
On doit également apporter une légère modification à la classe WebSecurityConfiguration
.
@KeycloakConfiguration
public class WebSecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter {
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
KeycloakAuthenticationProvider keycloakAuthenticationProvider =
keycloakAuthenticationProvider();
keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
auth.authenticationProvider(keycloakAuthenticationProvider);
}
@Bean
@Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new NullAuthenticatedSessionStrategy();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.csrf()
.disable()
.authorizeRequests()
.antMatchers(HttpMethod.DELETE,"/plant/**")
.hasRole("admin")
.anyRequest()
.authenticated()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
On ajoute un nouvel antMatcher qui restreint toutes les routes commençant par '/plant/' et qui utilise la méthode DELETE HTTP, ce qui correspond au endpoint deletePlant qu'on a ajouté précédemment.
On remarque que la correspondance des rôles est effectuée à l'aide de SimpleAuthorityMapper
. Par défaut, Spring Security ajoute un préfixe 'ROLE_' à toute autorité, mais les rôles de Keycloak ne le font pas.
En utilisant ce mappage, le préfixe sera ajouté à toute autorité envoyée dans le jeton Keycloak s'il n'est pas déjà présent.
Configuration Keycloak
On a déjà l'utilisateur 'admin' du blog précédent. Cet utilisateur a déjà le rôle 'admin'.
On doit créer un nouvel utilisateur qui n'a pas le rôle admin.
Il sera utilisé pour démontrer que notre autorisation basée sur les rôles fonctionne et que le point de terminaison DELETE sera interdit pour cet utilisateur.
Test de l'application
On commence par démarrer notre application.
mvn spring-boot:run
Pour tester notre configuration, on va utiliser la même méthode que dans le blog précédent et utiliser Postman comme client.
On améliore notre configuration Postman en ajoutant le nouvel utilisateur dans la collection de variables.
On vérifie d'abord que l'utilisateur 'user' sans le rôle admin ne peut pas accéder au endpoint de suppression.
On commence par récupérer le jeton avec l'utilisateur non administrateur.
On essaye d'utiliser l'endpoint de suppression en fournissant ce jeton dans l'en-tête d'autorisation.
Comme prévu, on reçoit une erreur 401 Non autorisé, car le rôle admin est manquant.
Cette fois-ci, on récupère un nouveau jeton avec l'utilisateur admin.
L'endpoint de suppression peut maintenant être utilisé avec succès.
Et voilà ! On a maintenant une autorisation basée sur les rôles.
Notez que depuis la version 5.7.0 de Spring Security, le WebSecurityConfigurerAdapter est deprecated. Il est désormais conseillé d'utiliser le nouveau type de configuration suivant la conception basée sur les composants. À l'heure actuelle, l'adaptateur Spring Boot Keycloak ne prend pas en charge ce nouveau type de configuration.
Le projet de démonstration est disponible sur Github.