Sécurité Spring Boot & JWT
Architecture de sécurité moderne : filtres stateless, JWT signé, refresh tokens, Spring Security 6, UserDetailsService propre, RBAC & bonnes pratiques industrielles.
Introduction
Spring Security est la référence pour sécuriser une API Spring Boot. Depuis la version 6, la configuration repose sur une approche 100% déclarative : SecurityFilterChain, stateless session, Bearer token, et JWT signé pour des API modernes et scalables.
Ce chapitre vous guide vers une implémentation professionnelle : architecture, filtres, tokens courts, refresh tokens sécurisés, rôles & permissions, logs, gestion des erreurs et bonnes pratiques.
Lexique essentiel
Token signé contenant l’identité et les rôles.
Chaîne de filtres qui sécurise les requêtes HTTP.
Responsable de l’authentification.
Charge l’utilisateur depuis la base.
Algorithme de hachage sécurisé pour les mots de passe.
Aucune session serveur → token nécessaire à chaque requête.
1. Configuration Spring Security 6 (stateless)
@Configuration
@EnableMethodSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http,
JwtFilter jwtFilter) throws Exception {
return http
.csrf(csrf -> csrf.disable())
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/auth/**").permitAll()
.anyRequest().authenticated())
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
.build();
}
}
👉 Stateless obligatoire pour les API modernes.
👉 Les routes /auth/** sont ouvertes pour le login & refresh.
👉 Le filtre JWT analyse chaque requête.
2. DTO d’authentification propre
public record LoginRequest(String email, String password) {}
public record AuthResponse(String token, String refreshToken) {}
3. Service utilisateur — niveau pro
@Service
@RequiredArgsConstructor
public class CustomUserDetailsService implements UserDetailsService {
private final UserRepository repo;
@Override
public UserDetails loadUserByUsername(String email) {
return repo.findByEmail(email)
.map(UserPrincipal::new)
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
}
}
Le UserPrincipal encapsule rôles et permissions pour la sécurité.
4. Génération du JWT signé (token court)
@Component
@RequiredArgsConstructor
public class JwtService {
@Value("${app.jwt.secret}")
private String secret;
public String generateToken(UserDetails user) {
return Jwts.builder()
.setSubject(user.getUsername())
.claim("roles", user.getAuthorities())
.setExpiration(Date.from(Instant.now().plusSeconds(900))) // 15 min
.signWith(Keys.hmacShaKeyFor(secret.getBytes()), SignatureAlgorithm.HS256)
.compact();
}
}
👉 Les tokens doivent être courts (10–20 min). 👉 Un refresh token gère la reconnexion sécurisée.
5. Le filtre JWT stateless
@Component
@RequiredArgsConstructor
public class JwtFilter extends OncePerRequestFilter {
private final JwtService jwtService;
private final CustomUserDetailsService userService;
@Override
protected void doFilterInternal(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain)
throws ServletException, IOException {
String header = req.getHeader("Authorization");
if (header == null || !header.startsWith("Bearer ")) {
chain.doFilter(req, res);
return;
}
String token = header.substring(7);
String email = jwtService.extractUsername(token);
UserDetails user = userService.loadUserByUsername(email);
UsernamePasswordAuthenticationToken auth =
new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(auth);
chain.doFilter(req, res);
}
}
Ce filtre exécute l’authentification sur chaque requête — modèle stateless.
- Token très court + refresh token long stocké côté client.
- Mot de passe haché via BCrypt.
- Ne jamais stocker un JWT dans localStorage (XSS).
- Aucune session → 100% stateless.
- Endpoints versionnés : /api/v1/**
2. Refresh Token sécurisé (niveau entreprise)
Le refresh token permet de ré-obtenir un JWT sans forcer l’utilisateur à se reconnecter. Il doit être : long, sécurisé, stocké HTTP Only et renouvelé périodiquement.
Exemple d’implémentation
@PostMapping("/refresh")
public AuthResponse refresh(@CookieValue("refresh") String refreshToken) {
var email = jwtService.verifyRefresh(refreshToken);
var user = userService.loadUserByUsername(email);
return new AuthResponse(
jwtService.generateToken(user),
jwtService.generateRefreshToken(email)
);
}
👉 Le refresh **ne doit jamais transiter dans le body** → cookie HttpOnly obligatoire. 👉 Rotation des refresh tokens : un nouveau est généré à chaque utilisation.
Quiz : Sécurité Spring Boot
-
Q1. Quel mode est recommandé pour les API modernes ?
- A. Stateful
- B. Stateless ✔
- C. Session redis
-
Q2. Où doit être stocké un refresh token ?
- A. localStorage
- B. Cookie HttpOnly ✔
- C. Header
Exercice d’application
Implémentez une sécurité complète pour votre API :
— Login + token court
— Refresh token HttpOnly
— Endpoints sécurisés + RBAC (@PreAuthorize)
— Logging de sécurité
— Rotation des refresh tokens
Résumé
Spring Security & JWT permettent de construire une architecture d’authentification moderne, robuste et scalable. Le modèle stateless, couplé à un token court et un refresh token sécurisé, garantit performance, sécurité et simplicité opérationnelle.
