Retour aux articles

Sécurisation avancée des API REST Spring Boot 3 avec Spring Security 6 et JWT

Sécurisation avancée des API REST Spring Boot 3 avec Spring Security 6 et JWT | Laty Gueye Samba - Développeur Full Stack Dakar Sénégal, Expert Java Spring Boot Angular

Sécurisation avancée des API REST Spring Boot 3 avec Spring Security 6 et JWT

Cet article présente une approche pratique et moderne pour sécuriser des API REST construites avec Spring Boot 3 en utilisant Spring Security 6 et des tokens JWT. Les exemples couvrent la configuration de sécurité, la génération et la validation des JWT, l'intégration d'un filtre d'authentification et les bonnes pratiques pour maintenir une API robuste et évolutive.

Contexte et objectifs

Le besoin principal consiste à protéger les ressources exposées par une API REST tout en gardant l'architecture sans état (stateless). Les objectifs sont : authentification par JWT, autorisation basée sur les rôles, protection des endpoints sensibles et prévention des attaques courantes (replay, usurpation, exposition de secret).

Dépendances essentielles

Les dépendances minimales pour un projet Maven :

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> <version>0.11.5</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-impl</artifactId> <version>0.11.5</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-jackson</artifactId> <version>0.11.5</version> </dependency>

Configuration de Spring Security 6

Spring Security 6 privilégie la configuration déclarative via des beans SecurityFilterChain. La configuration suivante désactive le stockage de session, applique une stratégie stateless et ajoute un filtre personnalisé pour traiter les tokens JWT.

@Configuration public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http, JwtAuthenticationFilter jwtFilter) throws Exception { http .csrf().disable() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeHttpRequests() .requestMatchers("/auth/**").permitAll() .requestMatchers("/admin/**").hasRole("ADMIN") .anyRequest().authenticated() .and() .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class); return http.build(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }

Génération et validation JWT

Un utilitaire JWT doit être responsable de la création, de la signature et de la validation des tokens. Il est conseillé d'utiliser une clé secrète correctement stockée (ex : vault, variable d'environnement) et une durée de vie adaptée.

public class JwtUtils { private final SecretKey key = Keys.hmacShaKeyFor(Decoders.BASE64.decode(System.getenv("JWT_SECRET"))); public String generateToken(String username, List<String> roles) { return Jwts.builder() .setSubject(username) .claim("roles", roles) .setIssuedAt(new Date()) .setExpiration(Date.from(Instant.now().plus(1, ChronoUnit.HOURS))) .signWith(key, SignatureAlgorithm.HS256) .compact(); } public Claims validateAndGetClaims(String token) { return Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody(); } }

Filtre d'authentification JWT

Le filtre extrait le token Authorization Bearer, valide le JWT et construit un objet Authentication portant les rôles. En cas d'échec, la requête est rejetée avec un code 401.

public class JwtAuthenticationFilter extends OncePerRequestFilter { private final JwtUtils jwtUtils; private final UserDetailsService userDetailsService; public JwtAuthenticationFilter(JwtUtils jwtUtils, UserDetailsService uds) { this.jwtUtils = jwtUtils; this.userDetailsService = uds; } @Override protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws ServletException, IOException { String header = req.getHeader("Authorization"); if (header != null && header.startsWith("Bearer ")) { String token = header.substring(7); try { Claims claims = jwtUtils.validateAndGetClaims(token); String username = claims.getSubject(); List<String> roles = claims.get("roles", List.class); UserDetails user = userDetailsService.loadUserByUsername(username); UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(user, null, roles.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList())); SecurityContextHolder.getContext().setAuthentication(auth); } catch (JwtException e) { res.setStatus(HttpServletResponse.SC_UNAUTHORIZED); return; } } chain.doFilter(req, res); } }

Gestion des refresh tokens et sécurité renforcée

Pour améliorer la sécurité, séparer access tokens et refresh tokens. Les refresh tokens doivent être stockés côté serveur ou en base chiffrée et révoqués en cas d'anomalie. Appliquer des protections complémentaires : rotation des clés, vérification d'empreinte du client (fingerprint), limitation du taux de requêtes et logging d'audit.

Bonnes pratiques et pièges à éviter

Il est crucial de : stocker la clé secrète hors du code source, limiter la durée de vie des access tokens, invalider les tokens lors d'un changement de mot de passe, éviter d'inclure des données sensibles dans le payload JWT et utiliser HTTPS pour toutes les communications.

Tests et débogage

Tester avec des outils comme Postman ou curl. Vérifier les en-têtes Authorization, la validité des signatures, les dates d'expiration et les claims. Mettre en place des tests d'intégration automatisés pour les chemins d'authentification et les contrôles d'accès.

Conclusion

La combinaison de Spring Boot 3, Spring Security 6 et JWT permet d'implémenter une sécurité moderne, performante et adaptée aux architectures sans état. Une implémentation soignée et des pratiques de sécurité rigoureuses garantissent la protection des ressources et une expérience API fiable pour les clients autorisés.

À 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