Dans l'écosystème du développement web moderne, la sécurité des APIs est une préoccupation primordiale. Les applications ne se contentent plus d'échanger des données en interne ; elles s'ouvrent souvent à des partenaires, des microservices et des clients externes. Assurer l'authentification et l'autorisation de manière robuste et évolutive est donc un impératif. Pour les développeurs Full Stack tels que Laty Gueye Samba, basé à Dakar, la maîtrise de ces concepts est essentielle pour construire des solutions résilientes.
Cet article se propose de guider les lecteurs à travers l'implémentation de Spring Security 6, combiné avec les JSON Web Tokens (JWT) et Keycloak, pour sécuriser une API développée avec Spring Boot 3.x. Cette approche fournit une solution d'authentification et d'autorisation centralisée, découplée et performante, idéale pour les architectures de microservices et les applications métier complexes.
L'utilisation de Keycloak comme Identity and Access Management (IAM) permet de déléguer la gestion des utilisateurs, des rôles et des sessions, tandis que Spring Security 6 gère l'intégration côté API en validant les JWT émis par Keycloak. Ce tandem offre une fondation solide pour la sécurité, comme en témoignent les projets où Laty Gueye Samba, Développeur Full Stack à Dakar, a mis en œuvre des solutions similaires pour des systèmes ERP ou des applications de gestion des risques.
Préparation de l'environnement Spring Boot et Keycloak
Configuration de Spring Boot
Pour démarrer, il est nécessaire d'ajouter les dépendances Spring Security et Spring OAuth2 Resource Server au projet Spring Boot. Cela se fait via le fichier pom.xml pour Maven ou build.gradle pour Gradle.
<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.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-jose</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
Ensuite, le fichier application.properties (ou application.yml) doit être configuré pour pointer vers l'instance Keycloak. Les propriétés essentielles incluent l'URL de l'émetteur (issuer-uri), qui permet à Spring Security de découvrir les clés publiques JWKS de Keycloak pour la validation des JWT.
spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8080/realms/mon-realm-keycloak
Il est important de s'assurer que l'URL http://localhost:8080/realms/mon-realm-keycloak/.well-known/openid-configuration est accessible et retourne la configuration OpenID Connect de Keycloak.
Configuration de Keycloak
Côté Keycloak, plusieurs étapes sont nécessaires :
- Création d'un Realm : Un realm représente un espace isolé pour la gestion des utilisateurs et des clients.
- Création d'un Client : Un client doit être créé pour l'API Spring Boot. Il est configuré comme un client public (par exemple, de type
openid-connect) et sonClient IDdoit correspondre à ce qui est attendu par l'application cliente. LesValid Redirect URIsetWeb Originssont importants pour les applications front-end qui interagissent avec Keycloak. - Création d'utilisateurs et de rôles : Des utilisateurs doivent être créés et se voir attribuer des rôles spécifiques au niveau du realm ou du client. Ces rôles seront ensuite inclus dans les JWT et utilisés par Spring Security pour l'autorisation.
Implémentation de Spring Security 6
Configuration de la chaîne de filtres de sécurité
La pièce maîtresse de la configuration de Spring Security 6 est la classe de configuration où le SecurityFilterChain est défini. Cette configuration utilise la DSL Lambda de Spring Security 6, offrant une syntaxe plus concise et lisible.
package com.latysamba.security;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
@EnableMethodSecurity // Permet d'utiliser @PreAuthorize
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(AbstractHttpConfigurer::disable) // Désactiver CSRF pour les APIs stateless
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/public/**").permitAll() // Exemples d'endpoints publics
.anyRequest().authenticated() // Toutes les autres requêtes nécessitent une authentification
)
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(jwt -> jwt
.jwtAuthenticationConverter(jwtAuthenticationConverter()) // Utiliser notre convertisseur de JWT
)
);
return http.build();
}
@Bean
public JwtAuthenticationConverter jwtAuthenticationConverter() {
JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
// Le préfixe "ROLE_" est une convention de Spring Security.
// Les rôles Keycloak sont souvent dans "realm_access.roles"
grantedAuthoritiesConverter.setAuthoritiesClaimName("realm_access.roles"); // Où trouver les rôles dans le JWT
grantedAuthoritiesConverter.setAuthorityPrefix("ROLE_"); // Ajouter un préfixe aux rôles
JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);
return jwtAuthenticationConverter;
}
}
Dans cet exemple, .csrf(AbstractHttpConfigurer::disable) désactive la protection CSRF, ce qui est courant pour les APIs RESTful et stateless. .anyRequest().authenticated() garantit que toutes les requêtes (sauf celles explicitement permises) nécessitent un jeton valide. La partie .oauth2ResourceServer(oauth2 -> oauth2.jwt(...)) configure Spring Security pour agir comme un serveur de ressources OAuth2, capable de valider les JWT.
Extraction des rôles JWT
Le JwtAuthenticationConverter est crucial pour que Spring Security puisse extraire les rôles ou autorités du JWT émis par Keycloak. Par défaut, Spring Security cherche les autorités dans le claim scope ou scp. Cependant, Keycloak place généralement les rôles d'un utilisateur dans le claim realm_access.roles (pour les rôles de realm) ou resource_access.<client-id>.roles (pour les rôles spécifiques à un client). Le convertisseur personnalisé dans le SecurityConfig ci-dessus indique à Spring de regarder dans realm_access.roles et d'ajouter le préfixe ROLE_, standard pour Spring Security.
Exposition d'une ressource protégée
Création d'un contrôleur sécurisé
Avec Spring Security configuré, il est possible de protéger les endpoints de l'API en utilisant des annotations comme @PreAuthorize au niveau des méthodes. Ceci est rendu possible grâce à l'annotation @EnableMethodSecurity dans la classe de configuration de sécurité.
package com.latysamba.controller;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api")
public class SecuredController {
@GetMapping("/hello-public")
public String sayHelloPublic() {
return "Bonjour depuis un endpoint public !";
}
@GetMapping("/hello-user")
@PreAuthorize("hasRole('USER')") // Nécessite le rôle USER
public String sayHelloUser() {
return "Bonjour, utilisateur authentifié !";
}
@GetMapping("/hello-admin")
@PreAuthorize("hasRole('ADMIN')") // Nécessite le rôle ADMIN
public String sayHelloAdmin() {
return "Bonjour, administrateur !";
}
}
Dans cet exemple, l'accès à /api/hello-user sera autorisé uniquement aux utilisateurs possédant le rôle USER (qui, après conversion, est ROLE_USER). De même, /api/hello-admin est réservé aux utilisateurs avec le rôle ADMIN.
Point de vue : développeur full stack à Dakar
Pour un développeur travaillant sur des systèmes de gestion hospitalière, des applications de gestion des risques ou des plateformes e-commerce au Sénégal, la maîtrise de Spring Security 6 avec JWT et Keycloak représente un avantage concurrentiel réel sur le marché technologique africain, en pleine expansion. L'intégration de ces technologies permet de bâtir des applications sécurisées et performantes, essentielles pour la confiance des utilisateurs et la conformité réglementaire.
Conclusion
L'implémentation de Spring Security 6 avec JWT et Keycloak pour une API Spring Boot 3.x offre une solution de sécurité robuste, flexible et prête pour l'entreprise. Cette approche permet de déléguer la complexité de la gestion des identités à un serveur IAM dédié, tout en bénéficiant de la puissance et de la flexibilité du cadre de sécurité de Spring. Pour un Développeur Full Stack à Dakar, Sénégal, et notamment pour un Expert Java Spring Boot Angular comme Laty Gueye Samba, comprendre et appliquer ces principes est fondamental pour la conception de systèmes modernes et sécurisés.
Il est toujours recommandé de consulter la documentation officielle pour les détails les plus récents et les meilleures pratiques :
À propos de l'auteur
Laty Gueye Samba est développeur Full Stack basé à Dakar, Sénégal. Spécialiste des écosystèmes Java / Spring Boot et Angular.
Contact : latygueyesamba@gmail.com | Dakar, Sénégal