Activité Pratique - Keycloak + Spring Boot
Implémentation d'une solution d'authentification et d'autorisation centralisée avec Keycloak et Spring Boot
Prérequis techniques
- Java 17+
- Maven 3.6+
- Spring Boot 3.x
- Docker
- Postman
- Node.js/NPM
Partie 1 : Configuration de Keycloak
1 Télécharger Keycloak 19
Keycloak : Solution d'identité et d'accès open source pour applications modernes et services RESTful.
Explication détaillée :
Keycloak est un serveur d'identité open source qui fournit une solution complète d'authentification et d'autorisation. Il implémente les standards OAuth 2.0, OpenID Connect et SAML 2.0. Keycloak permet de :
- Gérer les utilisateurs et leurs rôles
- Centraliser l'authentification pour plusieurs applications
- Fournir des jetons d'accès pour sécuriser les API
- Intégrer avec des fournisseurs d'identité externes (Google, Facebook, etc.)
La version 19 est choisie pour sa stabilité et sa compatibilité avec Spring Boot 3.x.
- Allez sur https://www.keycloak.org/downloads
- Téléchargez la version 19.0.3 (ou la dernière version 19.x)
- Extrayez l'archive dans un répertoire de votre choix
2 Démarrer Keycloak
Standalone mode : Mode d'exécution autonome de Keycloak pour développement/test.
Explication détaillée :
Le mode standalone de Keycloak est idéal pour le développement car il :
- Ne nécessite pas de serveur d'application externe
- Utilise une base de données H2 en mémoire pour les données de session
- Est rapide à démarrer et à configurer
- Permet de tester les fonctionnalités sans infrastructure complexe
Le mode start-dev est encore plus rapide et est spécifiquement conçu pour le développement.
./standalone.sh
Sur Windows :
.\standalone.bat
Ou avec le mode développement :
3 Créer un compte Admin
Realm Master : Compte administrateur principal pour gérer tous les realms.
Explication détaillée :
Le compte administrateur est le compte principal qui permet d'accéder à la console d'administration de Keycloak. Ce compte :
- A des droits étendus sur tous les realms
- Permet de créer et gérer des utilisateurs, rôles et clients
- Doit être sécurisé avec un mot de passe fort en production
- Est utilisé pour configurer l'infrastructure d'identité
Il est important de noter les identifiants car ils seront nécessaires pour accéder à la console d'administration.
- Ouvrez votre navigateur et allez sur
http://localhost:8080 - Cliquez sur "Administration Console"
- Créez un compte administrateur avec :
- Username: admin
- Password: admin123
- Email: admin@example.com
4 Créer une Realm
Realm : Espace de sécurité isolé contenant des utilisateurs, rôles, clients, etc.
Explication détaillée :
Un Realm dans Keycloak est un espace d'isolation logique qui :
- Contient un ensemble d'utilisateurs
- Gère ses propres rôles et permissions
- A ses propres configurations d'authentification
- Peut être comparé à un domaine de sécurité
Les realms permettent de séparer les environnements (dev, test, prod) ou les organisations différentes.
- Cliquez sur "Master" en haut à gauche
- Sélectionnez "Add realm"
- Entrez le nom : "spring-boot-realm"
- Cliquez sur "Create"
5 Créer un client à sécuriser
Client : Application ou service qui utilise Keycloak pour l'authentification.
Explication détaillée :
Un Client dans Keycloak représente une application ou un service qui utilise Keycloak pour l'authentification. Les paramètres importants sont :
- Client ID : Identifiant unique du client
- Access Type :
- public : Pour les applications client-side (Angular, React)
- confidential : Pour les applications serveur (Spring Boot)
- bearer-only : Pour les API qui n'initient pas l'authentification
- Standard Flow : Pour l'authentification avec redirection
- Direct Access Grants : Pour l'authentification par mot de passe direct
- Valid Redirect URIs : URLs autorisées pour la redirection après authentification
- Allez dans l'onglet "Clients"
- Cliquez sur "Create"
- Remplissez les champs :
- Client ID: "spring-boot-client"
- Client Protocol: "openid-connect"
- Cliquez sur "Save"
- Configurez les paramètres :
- Access Type: "confidential"
- Standard Flow Enabled: ON
- Direct Access Grants Enabled: ON
- Valid Redirect URIs: http://localhost:8080/*
- Allez dans l'onglet "Credentials"
- Copiez le "Secret" généré
6 Créer des utilisateurs
Utilisateur : Entité pouvant s'authentifier et accéder aux ressources protégées.
Explication détaillée :
Les utilisateurs dans Keycloak sont des entités qui peuvent :
- S'authentifier auprès du serveur Keycloak
- Recevoir des rôles et des attributs
- Accéder aux applications clientes configurées
- Avoir des informations personnelles stockées
La gestion des utilisateurs inclut la création de profils, la gestion des mots de passe et l'attribution de rôles.
- Allez dans l'onglet "Users"
- Cliquez sur "Add user"
- Remplissez les informations :
- Username: "user1"
- Email: "user1@example.com"
- First Name: "Jean"
- Last Name: "Utilisateur"
- Cliquez sur "Save"
- Allez dans l'onglet "Credentials"
- Entrez un mot de passe
- Décochez "Temporary" et sauvegardez
Répétez pour créer un utilisateur "admin1" avec rôle admin.
7 Créer des rôles
Rôle : Ensemble de permissions attribuables à des utilisateurs ou groupes.
Explication détaillée :
Les rôles dans Keycloak sont des unités de permission qui permettent de :
- Contrôler l'accès aux ressources
- Implémenter le contrôle d'accès basé sur les rôles (RBAC)
- Organiser les permissions de manière hiérarchique
- Attribuer des droits spécifiques aux utilisateurs
Les rôles peuvent être globaux (realm roles) ou spécifiques à un client (client roles).
- Allez dans l'onglet "Roles"
- Cliquez sur "Add Role"
- Créez les rôles suivants :
- Nom: "user"
- Nom: "admin"
8 Affecter les rôles aux utilisateurs
Role Mapping : Attribution de rôles à des utilisateurs pour contrôler les accès.
Explication détaillée :
Le Role Mapping est le processus d'attribution de rôles à des utilisateurs. Cela permet de :
- Déterminer les permissions d'un utilisateur
- Contrôler l'accès aux ressources protégées
- Implémenter des politiques d'autorisation fines
- Appliquer le principe du moindre privilège
Les rôles sont inclus dans les jetons JWT envoyés aux applications clientes.
- Allez dans l'onglet "Users"
- Sélectionnez un utilisateur
- Allez dans l'onglet "Role Mappings"
- Dans "Available Roles", sélectionnez les rôles appropriés
- Ajoutez-les aux "Assigned Roles"
9 Tests avec Postman
Postman : Outil de test d'API pour valider les flux d'authentification OAuth2/OpenID Connect.
Explication détaillée :
Postman est un outil essentiel pour tester les flux d'authentification OAuth2/OpenID Connect. Il permet de :
- Simuler des requêtes d'authentification
- Tester différents types de flux OAuth2
- Valider la structure des jetons JWT
- Vérifier la configuration du serveur Keycloak
Les tests permettent de s'assurer que l'authentification fonctionne correctement avant d'intégrer avec les applications.
9.1 Tester l'authentification avec le mot de passe
Resource Owner Password Credentials : Flux d'authentification directe avec identifiants.
Explication détaillée :
Le flux Resource Owner Password Credentials permet à un utilisateur de fournir directement ses identifiants (nom d'utilisateur et mot de passe) à l'application, qui les transmet ensuite au serveur d'autorisation pour obtenir un jeton. Ce flux :
- Est simple à implémenter
- Convient aux applications de confiance
- N'est pas recommandé pour les applications tierces
- Permet d'obtenir à la fois un access token et un refresh token
Ce flux est souvent utilisé pour les tests et les applications internes.
Body (x-www-form-urlencoded):
grant_type: password
client_id: spring-boot-client
client_secret: [VOTRE_SECRET_CLIENT]
username: user1
password: [MOT_DE_PASSE_UTILISATEUR]
9.2 Analyser les contenus des deux JWT Access Token et Refresh Token
JWT (JSON Web Token) : Jeton d'authentification au format JSON signé.
Explication détaillée :
Un JWT (JSON Web Token) est composé de trois parties :
- Header : Contient les métadonnées sur le token (type, algorithme de signature)
- Payload : Contient les claims (informations sur l'utilisateur, rôles, expiration)
- Signature : Permet de vérifier l'intégrité du token
L'Access Token contient des informations sur l'utilisateur authentifié et ses rôles. Le Refresh Token permet d'obtenir un nouvel access token sans réauthentification.
Utilisez jwt.io pour décoder les jetons reçus dans la réponse.
9.3 Tester l'authentification avec le Refresh Token
Refresh Token : Jeton permettant d'obtenir un nouveau jeton d'accès sans réauthentification.
Explication détaillée :
Le Refresh Token est un mécanisme de sécurité qui permet de :
- Renouveler les access tokens sans demander à l'utilisateur de se reconnecter
- Maintenir la session de manière transparente
- Réduire la fenêtre de vulnérabilité des access tokens
- Améliorer l'expérience utilisateur
Le refresh token a une durée de vie plus longue que l'access token et doit être stocké de manière sécurisée.
Body (x-www-form-urlencoded):
grant_type: refresh_token
client_id: spring-boot-client
client_secret: [VOTRE_SECRET_CLIENT]
refresh_token: [VOTRE_REFRESH_TOKEN]
9.4 Tester l'authentification avec Client ID et Client Secret
Client Credentials : Flux pour applications backend sans utilisateur final.
Explication détaillée :
Le flux Client Credentials est utilisé pour l'authentification machine-à-machine. Il :
- Permet à une application de s'authentifier sans utilisateur humain
- Est utilisé pour les services backend qui communiquent entre eux
- N'inclut pas d'informations utilisateur dans le jeton
- Donne accès basé sur l'identité du client, non de l'utilisateur
Ce flux est typiquement utilisé pour les API-to-API communication.
Body (x-www-form-urlencoded):
grant_type: client_credentials
client_id: spring-boot-client
client_secret: [VOTRE_SECRET_CLIENT]
9.5 Changer les paramètres des Tokens
Token Lifespan : Durée de validité des jetons d'accès et de rafraîchissement.
Explication détaillée :
La durée de vie des tokens est un paramètre de sécurité crucial qui :
- Réduit la fenêtre de vulnérabilité en cas de compromission d'un token
- Force la réauthentification régulière pour les tokens courts
- Équilibre sécurité et expérience utilisateur
- Peut être configurée différemment pour différents types de tokens
Des durées de vie trop longues augmentent le risque de sécurité, tandis que des durées trop courtes nuisent à l'expérience utilisateur.
- Allez dans Realm Settings
- Modifiez les durées de vie des jetons dans l'onglet "Tokens"
- Testez à nouveau les appels API
Partie 2 : Sécuriser une architecture micro-service Spring Angular avec Keycloak
2.1 Création de l'application Spring Boot
Spring Boot : Framework pour créer des applications Spring standalone.
OAuth2 Resource Server : Composant Spring pour valider les jetons OAuth2.
Explication détaillée :
Les dépendances nécessaires pour une application Spring Boot sécurisée avec OAuth2 sont :
- spring-boot-starter-web : Pour les applications web RESTful
- spring-boot-starter-security : Pour les fonctionnalités de sécurité de base
- spring-boot-starter-oauth2-resource-server : Pour valider les jetons OAuth2
Ces dépendances permettent à Spring Boot de fonctionner comme un serveur de ressources OAuth2, validant les jetons JWT reçus dans les requêtes.
<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>
</dependencies>
2.2 Configuration de sécurité
Issuer URI : URL du serveur d'identité qui a émis le jeton.
JWK Set URI : URL des clés de signature pour valider les jetons JWT.
Explication détaillée :
La configuration de sécurité OAuth2 comprend deux éléments essentiels :
- Issuer URI : L'URL du serveur Keycloak qui a émis le jeton. Spring Boot l'utilise pour valider que le jeton provient d'une source de confiance.
- JWK Set URI : L'URL des clés publiques (JWK - JSON Web Key) que Spring Boot utilise pour vérifier la signature du jeton JWT.
Ces paramètres permettent à Spring Boot de valider automatiquement les jetons JWT sans configuration manuelle des clés.
security:
oauth2:
resourceserver:
jwt:
issuer-uri: http://localhost:8080/realms/spring-boot-realm
jwk-set-uri: http://localhost:8080/realms/spring-boot-realm/protocol/openid-connect/certs
server:
port: 8081
2.3 Configuration de sécurité Spring
SecurityFilterChain : Composant Spring Security pour définir les règles d'accès.
JWT Decoder : Composant pour décoder et valider les jetons JWT.
Explication détaillée :
La SecurityFilterChain est le composant central de Spring Security qui :
- Définit les règles d'autorisation pour différentes URL
- Active la gestion OAuth2/JWT pour les requêtes sécurisées
- Intègre automatiquement la validation des jetons JWT
- Mappe les rôles Keycloak aux rôles Spring Security
La méthode Customizer.withDefaults() configure automatiquement le décodeur JWT en utilisant les URIs configurés précédemment.
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authz -> authz
.requestMatchers("/public/**").permitAll()
.requestMatchers("/user/**").hasRole("user")
.requestMatchers("/admin/**").hasRole("admin")
.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(Customizer.withDefaults())
);
return http.build();
}
}
2.4 Contrôleur sécurisé
@PreAuthorize : Annotation Spring Security pour contrôler l'accès aux méthodes.
Authentication : Objet Spring contenant les détails de l'utilisateur authentifié.
Explication détaillée :
Les annotations et objets de sécurité Spring Security permettent de :
- @PreAuthorize : Contrôler l'accès aux méthodes basé sur des expressions SpEL (Spring Expression Language)
- Authentication : Accéder aux détails de l'utilisateur authentifié (nom, rôles, etc.)
- Implémenter des contrôles d'accès fins au niveau des méthodes
- Extraire dynamiquement les informations utilisateur des jetons JWT
Les rôles sont automatiquement extraits du jeton JWT et mis à disposition pour les vérifications d'autorisation.
@RequestMapping("/api")
public class SecureController {
@GetMapping("/public/info")
public String publicInfo() {
return "Ceci est une information publique";
}
@GetMapping("/user/profile")
@PreAuthorize("hasRole('USER')")
public String userProfile(Authentication authentication) {
return "Profil utilisateur: " + authentication.getName();
}
@GetMapping("/admin/users")
@PreAuthorize("hasRole('ADMIN')")
public String adminUsers() {
return "Liste des utilisateurs (accès admin requis)";
}
}
2.5 Création de l'application Angular
Angular : Framework TypeScript pour créer des applications web dynamiques.
keycloak-js : Bibliothèque JavaScript pour intégrer Keycloak dans des applications frontend.
Explication détaillée :
Angular est choisi comme framework frontend car il :
- Offre une structure modulaire et scalable
- Intègre bien avec les API RESTful
- Permet une séparation claire entre la logique métier et la présentation
- S'intègre facilement avec Keycloak via la bibliothèque keycloak-js
La bibliothèque keycloak-js fournit une interface JavaScript pour interagir avec le serveur Keycloak.
cd keycloak-angular-app
npm install keycloak-js
2.6 Configuration Keycloak dans Angular
Keycloak Instance : Client JavaScript pour interagir avec le serveur Keycloak.
login-required : Paramètre pour forcer l'authentification à l'initialisation.
Explication détaillée :
Le service Keycloak dans Angular gère :
- La configuration de la connexion au serveur Keycloak
- L'initialisation de la session utilisateur
- La gestion des jetons d'accès
- La redirection vers la page de login si nécessaire
Le paramètre onLoad: 'login-required' force l'utilisateur à se connecter immédiatement après le chargement de l'application.
import { Injectable } from '@angular/core';
import * as Keycloak from 'keycloak-js';
@Injectable({
providedIn: 'root'
})
export class KeycloakService {
private keycloakAuth: Keycloak.KeycloakInstance;
constructor() {
this.keycloakAuth = Keycloak({
url: 'http://localhost:8080',
realm: 'spring-boot-realm',
clientId: 'spring-boot-client'
});
}
init(): Promise<any> {
return new Promise((resolve, reject) => {
this.keycloakAuth.init({ onLoad: 'login-required' })
.then(() => {
resolve(this.keycloakAuth);
})
.catch(error => {
reject(error);
});
});
}
getToken(): string {
return this.keycloakAuth.token;
}
}
2.7 Intégration dans le module principal
APP_INITIALIZER : Injection de dépendance pour exécuter du code avant le démarrage de l'application.
kcFactory : Fonction factory pour initialiser le service Keycloak.
Explication détaillée :
L'APP_INITIALIZER est un mécanisme Angular qui permet d'exécuter du code avant que l'application ne démarre. Cela est essentiel pour :
- Initialiser la session Keycloak avant le chargement des composants
- Vérifier si l'utilisateur est déjà connecté
- Effectuer la redirection de login si nécessaire
- Assurer que les services d'authentification sont prêts
La fonction factory garantit que le service Keycloak est initialisé avant que l'application ne commence à fonctionner.
import { APP_INITIALIZER } from '@angular/core';
import { KeycloakService } from './keycloak.service';
export function kcFactory(kcService: KeycloakService) {
return () => kcService.init();
}
@NgModule({
...
providers: [
KeycloakService,
{
provide: APP_INITIALIZER,
useFactory: kcFactory,
deps: [KeycloakService],
multi: true
}
]
})
2.8 Service HTTP avec jeton
HttpInterceptor : Interception des requêtes HTTP pour ajouter des en-têtes d'authentification.
Bearer Token : Schéma d'authentification utilisant des jetons JWT.
Explication détaillée :
L'HttpInterceptor est un composant Angular qui permet d'intercepter et de modifier les requêtes HTTP sortantes. Il est utilisé pour :
- Ajouter automatiquement le jeton d'accès aux en-têtes Authorization
- Assurer que toutes les requêtes vers les API sécurisées incluent le jeton
- Centraliser la gestion de l'authentification pour toutes les requêtes
- Implémenter des stratégies de gestion des erreurs d'authentification
Le schéma Bearer est le standard pour l'authentification par jeton JWT.
import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler } from '@angular/common/http';
import { KeycloakService } from './keycloak.service';
@Injectable()
export class TokenInterceptorService implements HttpInterceptor {
constructor(private keycloakService: KeycloakService) {}
intercept(req: HttpRequest<any>, next: HttpHandler) {
const token = this.keycloakService.getToken();
if (token) {
req = req.clone({
setHeaders: {
Authorization: `Bearer ${token}`
}
});
}
return next.handle(req);
}
}
2.9 Architecture finale
Architecture Microservice : Approche de développement avec services indépendants.
Single Sign-On (SSO) : Authentification unique pour plusieurs applications.
Explication détaillée :
L'architecture finale implémente un modèle de sécurité moderne :
- Keycloak serveur : Fournit l'authentification centralisée et la gestion des jetons
- Microservice Spring Boot : Fournit les API sécurisées qui valident les jetons JWT
- Application Angular : Interface utilisateur qui gère la session utilisateur et les jetons
- Communication sécurisée : Toutes les interactions utilisent des jetons JWT signés
Cette architecture permet de bénéficier de Single Sign-On (SSO), où un utilisateur authentifié peut accéder à plusieurs applications sans se reconnecter.
- Keycloak serveur (authentification centralisée)
- Microservice Spring Boot (ressources protégées)
- Application Angular (interface utilisateur)
- Communication sécurisée via JWT