Retour aux articles

Implémentation avancée de Spring Security 6 avec JWT et Spring Boot 3.x pour API REST

Implémentation avancée de Spring Security 6 avec JWT et Spring Boot 3.x pour API REST | Laty Gueye Samba - Développeur Full Stack Dakar Sénégal, Expert Java Spring Boot Angular
```html

Implémentation avancée de Spring Security 6 avec JWT et Spring Boot 3.x pour API REST

Les applications modernes exposant des API REST nécessitent une authentification stateless, fiable et facile à faire évoluer. Spring Security 6, associé à Spring Boot 3.x et à des JWT (JSON Web Tokens), fournit une base robuste pour sécuriser les endpoints tout en conservant une architecture scalable.

Objectifs techniques

Cette approche vise à :

  • Configurer une chaîne de sécurité Spring Security 6 adaptée aux API REST
  • Mettre en place un JWT filter (authentification stateless)
  • Gérer les rôles, les claims et l’autorisation fine
  • Assurer une validation stricte : signature, expiration, audience/issuer
  • Produire des réponses d’erreur cohérentes (401/403) et traçables

Prérequis

Les éléments suivants sont attendus :

  • Java 17+ (recommandé avec Spring Boot 3.x)
  • Spring Boot 3.x et Spring Security 6
  • Bibliothèque JWT (ex. io.jsonwebtoken ou nimbus-jose-jwt)
  • Une stratégie de stockage des secrets (env variables, vault, etc.)

Dépendances Maven (exemple)

org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-security io.jsonwebtoken jjwt-api 0.12.5 io.jsonwebtoken jjwt-impl 0.12.5 runtime io.jsonwebtoken jjwt-jackson 0.12.5 runtime ]]>

Architecture recommandée

L’approche se structure généralement en quatre blocs :

  1. Provider / Service JWT : création et validation des tokens
  2. Filter JWT : extraction du bearer token et authentification
  3. Configuration Security (SecurityFilterChain) : règles d’accès et désactivation CSRF en REST
  4. Gestion d’erreurs : handlers d’authentification et d’accès

Modèle de claims JWT

Un design de claims efficace facilite l’autorisation. Par exemple :

  • sub : identifiant utilisateur
  • iss : émetteur
  • aud : audience (optionnel mais recommandé)
  • exp : expiration
  • roles : liste des rôles
  • scope : scopes optionnels (plus fin)

Service JWT : création et validation

Le service JWT encapsule la logique de signature et de parsing. Une validation stricte renforce la sécurité (signature, expiration, issuer, audience).

Exemple de service JWT

roles, long ttlMillis) { long now = System.currentTimeMillis(); Date issuedAt = new Date(now); Date expiration = new Date(now + ttlMillis); return Jwts.builder() .setSubject(subject) .setIssuer(issuer) .setAudience(audience) .setIssuedAt(issuedAt) .setExpiration(expiration) .claim("roles", roles) .signWith(signingKey, SignatureAlgorithm.HS256) .compact(); } public Jws validateAndParse(String token) { JwtParser parser = Jwts.parserBuilder() .setSigningKey(signingKey) .requireIssuer(issuer) .requireAudience(audience) .build(); return parser.parseClaimsJws(token); } public boolean isExpired(String token) { try { validateAndParse(token); return false; } catch (ExpiredJwtException ex) { return true; } } } ]]>

Filter JWT : authentification stateless

Le filter inspecte l’en-tête Authorization, extrait le token de type Bearer, valide la signature et reconstruit le contexte de sécurité.

Exemple de JWT Authentication Filter

jws = jwtService.validateAndParse(token); Claims claims = jws.getBody(); String subject = claims.getSubject(); @SuppressWarnings("unchecked") List roles = claims.get("roles", List.class); List authorities = roles.stream() .map(r -> r.startsWith("ROLE_") ? r : "ROLE_" + r) .map(SimpleGrantedAuthority::new) .collect(Collectors.toList()); var authentication = new UsernamePasswordAuthenticationToken(subject, null, authorities); SecurityContextHolder.getContext().setAuthentication(authentication); } catch (io.jsonwebtoken.JwtException | IllegalArgumentException ex) { // Token invalide, expiration, issuer/audience incorrect... SecurityContextHolder.clearContext(); // La gestion finale 401 est déléguée au AuthenticationEntryPoint/Handler } filterChain.doFilter(request, response); } } ]]>

Configuration Spring Security 6 (SecurityFilterChain)

En Spring Security 6, la configuration passe typiquement par un bean SecurityFilterChain. Pour une API REST stateless, il est fréquent de désactiver CSRF et de configurer la gestion d’erreurs.

Exemple de configuration complète

response.sendError(org.springframework.http.HttpStatus.FORBIDDEN.value()); http .csrf(csrf -> csrf.disable()) .sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .exceptionHandling(ex -> ex .authenticationEntryPoint(authEntryPoint) .accessDeniedHandler(accessDeniedHandler) ) .authorizeHttpRequests(auth -> auth // Public endpoints .requestMatchers(new AntPathRequestMatcher("/api/auth/**")).permitAll() .requestMatchers(HttpMethod.GET, "/api/public/**").permitAll() // Example: stricte séparation .requestMatchers(HttpMethod.POST, "/api/admin/**").hasRole("ADMIN") .requestMatchers("/api/user/**").hasAnyRole("USER", "ADMIN") // Le reste requiert une authentification .anyRequest().authenticated() ) .addFilterBefore(jwtAuthenticationFilter, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.class) .httpBasic(Customizer.withDefaults()) .formLogin(form -> form.disable()); return http.build(); } } ]]>

Autorisation avancée : méthode et règles fines

Une pratique robuste consiste à combiner : des règles au niveau du router (SecurityFilterChain) et des règles au niveau méthode (annotations).

Exemple d’annotation method-level

Vérification de claims spécifiques

Lorsque l’autorisation dépend d’un attribut métier (ex. organisation, tenant, identifiant), l’approche avancée consiste à exposer des informations issues des claims dans le contexte puis à les exploiter via SpEL.

Exemple conceptuel : ajouter un claim tenant et vérifier l’accès selon le tenant cible. (La mise en place dépend du modèle d’application et du mapping des claims.)

Gestion des erreurs : réponse JSON cohérente

Pour une API REST, il est utile de renvoyer des erreurs structurées. Le handler peut produire une réponse JSON lors de : 401 Unauthorized (token invalide/absent) et 403 Forbidden (permissions insuffisantes).

Exemple d’entry point 401 JSON

Conseils de sécurité (checklist)

  • Secrets robustes : longueur minimale correcte pour HMAC (éviter les clés faibles)
  • Rotation : prévoir une rotation de clé et la gestion multi-clés si nécessaire
  • Validation stricte : vérifier exp, iss, aud, signature
  • Durée de vie : TTL court + mécanisme de renouvellement (refresh token) si requis
  • Révocation : JWT stateless implique une stratégie explicite de révocation/blacklist si nécessaire
  • Logs et traçabilité : éviter l’exposition des tokens dans les logs
  • Least privilege : rôles/scope minimaux

Exemple de flux d’authentification

Étapes typiques

1. Le client appelle /api/auth/login.

2. Le serveur émet un JWT signé avec les claims nécessaires.

3. Le client inclut Authorization: Bearer <token> sur chaque requête.

4. Le filter valide le token et injecte le Authentication dans le contexte Spring.

5. Les règles d’accès autorisent ou refusent selon les rôles et/ou conditions méthode.

Conclusion

Une implémentation avancée de Spring Security 6 avec JWT pour Spring Boot 3.x repose sur une configuration stateless, un filter dédié à la validation JWT, une autorisation combinant règles de route et contrôles au niveau méthode, et une gestion d’erreurs adaptée aux API REST. Cette approche fournit un compromis solide entre sécurité, maintenabilité et évolutivité.

À 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

© 2026 Laty Gueye Samba.