JWT Spring Security - Évitez les erreurs courantes en production

Rémy Bonneau .

27 mars 2026

Connexion réussie avec spring security JWT. Le corps de la requête POST contient les identifiants, la réponse renvoie un token JWT.

Mettre en place une authentification par jetons dans une API Spring ne consiste pas seulement à brancher une dépendance et à ouvrir quelques endpoints. Il faut comprendre ce que Spring Security valide réellement, ce que le client transporte, et où se trouvent les vrais points de fragilité en production. Je vais aller droit au but: comment fonctionne le flux JWT, comment le configurer proprement, quels claims vérifier, comment convertir ces informations en droits, et quels arbitrages faire entre JWT, jeton opaque et session.

Les points à verrouiller avant de confier des jetons à Spring Security

  • Spring Security agit ici comme resource server et valide un Bearer token, pas comme serveur d’identité.
  • La signature ne suffit pas: iss, aud, exp et nbf doivent être contrôlés explicitement.
  • Les scopes et claims métiers doivent être transformés en autorités exploitables par Spring Security.
  • Un access token court est généralement plus sain, souvent 5 à 15 minutes selon le niveau de risque.
  • JWT est pratique pour les APIs distribuées, mais la révocation immédiate reste plus simple avec un jeton opaque ou une session.

Diagramme de flux pour la sécurité Spring avec un filtre JwtTokenFilter gérant les requêtes et les réponses JWT.

Ce que Spring Security fait réellement avec un jeton JWT

Le point le plus important à garder en tête est simple: Spring Security ne “fait pas confiance” au jeton, il le vérifie. Dans une API protégée, le client envoie en général un en-tête Authorization: Bearer ..., puis la chaîne de filtres extrait ce jeton, le valide et construit un contexte d’authentification pour la requête en cours.

  1. Le client appelle l’API avec un jeton Bearer.
  2. Le filtre dédié extrait le jeton et le remet à la chaîne d’authentification.
  3. Le validateur vérifie la signature avec la clé publique attendue, puis contrôle les claims critiques.
  4. Les claims utiles sont transformés en autorités ou en identités applicatives.
  5. Spring Security place le résultat dans le SecurityContext et laisse les règles d’autorisation décider.

Je vois souvent une confusion à ce stade: un JWT signé n’est pas un coffre-fort. Il garantit l’intégrité et l’authenticité du message, pas sa confidentialité. Si le token est un JWS classique, son contenu reste lisible; il faut donc éviter d’y mettre des données sensibles que vous ne voudriez pas exposer à un client légitime mais curieux. Une fois ce mécanisme clarifié, la vraie question devient la configuration minimale qui tient la route.

Configurer un resource server proprement

Dans la pratique, la configuration la plus robuste commence par un principe: l’API consomme un serveur d’autorisation, elle ne réinvente pas l’authentification. Avec Spring Boot, cela se traduit généralement par l’ajout du starter resource server, puis par la déclaration de l’émetteur du jeton et, si nécessaire, de l’audience attendue.

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: https://idp.exemple.fr/realms/produit
          audiences: api-backoffice

Cette configuration est volontairement sobre. Quand issuer-uri est connu, Spring peut découvrir les métadonnées nécessaires, récupérer le jeu de clés publiques et vérifier que le jeton vient bien de l’émetteur attendu. En d’autres termes, vous évitez de coder en dur une logique de validation fragile, et vous laissez le framework faire ce pour quoi il est conçu.

@Bean
SecurityFilterChain apiSecurity(HttpSecurity http) throws Exception {
    http
        .csrf(csrf -> csrf.disable())
        .authorizeHttpRequests(auth -> auth
            .requestMatchers("/api/public/**").permitAll()
            .requestMatchers("/api/admin/**").hasAuthority("SCOPE_admin.read")
            .anyRequest().authenticated()
        )
        .oauth2ResourceServer(oauth2 -> oauth2
            .jwt(Customizer.withDefaults()));
    return http.build();
}

Le détail qui change tout, c’est que je n’utilise pas cette configuration pour “fabriquer” l’authentification, mais pour la restreindre correctement. Une API trop permissive fonctionne en test; en production, elle devient vite un point de passage trop ouvert. Avec cette base, il faut maintenant regarder ce qu’il faut vérifier dans le jeton lui-même.

Valider les claims qui comptent vraiment

Beaucoup d’équipes s’arrêtent à la signature, puis découvrent trop tard qu’un jeton valide peut quand même être inadapté au contexte. Je vérifie systématiquement les claims qui définissent le cadre d’usage du jeton, pas seulement son intégrité cryptographique.

Claim Rôle Ce que je contrôle
iss Émetteur du jeton Je n’accepte qu’un émetteur de confiance, explicitement prévu.
aud Audience visée Je refuse un jeton émis pour un autre service.
exp Expiration Je privilégie des durées courtes et j’exige un rejet net après expiration.
nbf Pas encore valide avant Je limite la tolérance d’horloge à quelques dizaines de secondes au maximum.
sub Sujet principal Je l’utilise comme identifiant stable, pas comme libellé décoratif.

Je fixe aussi les algorithmes acceptés. Ce point est souvent sous-estimé: laisser une application “accepter ce qu’elle sait lire” est une mauvaise idée. Je préfère une politique nette, des clés JWK maîtrisées, et une rotation testée avant d’en avoir besoin. Si vous avez plusieurs émetteurs ou plusieurs tenants, Spring Security sait aussi gérer ce cas, mais il faut alors être rigoureux sur la résolution du bon émetteur et sur la propagation du tenant dans le reste du traitement.

Sur les durées, mon repère pratique est simple: un access token court, souvent entre 5 et 15 minutes, puis un mécanisme de renouvellement séparé si le besoin métier le justifie. Plus le jeton vit longtemps, plus le compromis sécurité/expérience utilisateur devient sensible. Une fois ce cadre posé, il reste à transformer les claims en droits réellement exploitables par l’application.

Transformer les claims en autorisations utiles

L’étape suivante n’est pas cosmétique: c’est ce qui relie le jeton aux règles métier. Spring Security sait convertir des claims en authorities, et c’est là que les scopes, rôles et permissions deviennent réellement utilisables dans les règles d’accès.

Par défaut, les scopes sont souvent exposés avec un préfixe SCOPE_. C’est pratique pour des API simples, par exemple SCOPE_orders.read ou SCOPE_invoices.write. Dès que le schéma d’autorisations devient plus métier, je préfère un mapping explicite, surtout si le fournisseur d’identité utilise un claim personnalisé.

@Bean
JwtAuthenticationConverter jwtAuthenticationConverter() {
    JwtGrantedAuthoritiesConverter authorities = new JwtGrantedAuthoritiesConverter();
    authorities.setAuthoritiesClaimName("authorities");
    authorities.setAuthorityPrefix("ROLE_");

    JwtAuthenticationConverter converter = new JwtAuthenticationConverter();
    converter.setJwtGrantedAuthoritiesConverter(authorities);
    converter.setPrincipalClaimName("preferred_username");
    return converter;
}

Ce code est intéressant pour une raison précise: il sépare l’identité technique du jeton et les autorisations opérationnelles. Je peux donc authentifier un utilisateur avec sub ou preferred_username, tout en gardant des règles d’autorisation lisibles dans le code, par exemple avec @PreAuthorize ou des règles HTTP ciblées. Cette séparation évite une erreur fréquente: confondre un identifiant de sujet avec un rôle.

Quand je relis une base Spring Security mal pensée, le problème n’est presque jamais l’absence de JWT; c’est un mapping trop flou entre ce que contient le jeton et ce que l’application croit pouvoir faire avec. C’est justement là que les incidents commencent.

Les erreurs qui reviennent le plus souvent en production

Je retrouve toujours les mêmes défauts, quel que soit le projet ou la taille de l’équipe. Le premier est de croire qu’un JWT signé est automatiquement sûr parce qu’il est “cryptographique”. En réalité, il peut être parfaitement signé et pourtant mal ciblé, trop long à vivre, ou porteur d’informations mal choisies.

Erreur Pourquoi c’est risqué Ce que je fais à la place
Ne pas vérifier aud Un jeton émis pour un autre service peut être accepté à tort. Je fixe une audience précise et je la rejette si elle ne correspond pas.
Allonger trop la durée de vie Une compromission reste exploitable trop longtemps. Je garde des access tokens courts et je sépare le renouvellement.
Mettre des données sensibles dans le payload Le contenu d’un JWT signé reste lisible. Je limite le payload au strict nécessaire.
Loguer le token complet Les journaux deviennent une fuite de secrets. Je masque le jeton dans les logs et les traces.
Accepter plusieurs comportements d’algorithme sans règle claire La validation devient floue et plus difficile à auditer. Je fixe les algorithmes attendus et je teste les refus.

Il y a aussi un piège de design côté navigateur. Si vous stockez le jeton dans un cookie, vous devez penser CSRF. Si vous le gardez dans le stockage web côté client, vous devez penser XSS. Il n’existe pas de choix magique; il existe seulement un compromis cohérent avec votre surface d’attaque. C’est aussi pour cela qu’il faut choisir la bonne forme de jeton plutôt que d’imposer JWT partout par réflexe.

Choisir entre JWT, jeton opaque et session selon le contexte

La bonne question n’est pas “JWT ou pas JWT”, mais quel niveau de contrôle du cycle de vie du jeton l’équipe veut réellement assumer. Quand je compare les options, je regarde surtout la révocation, l’indépendance réseau et la simplicité d’exploitation.

Approche Quand je la choisis Point fort Limite principale
JWT APIs distribuées, plusieurs services, besoin de validation locale Stateless, rapide à vérifier, adapté aux architectures découpées Révocation moins immédiate, vigilance accrue sur les claims et la durée de vie
Jeton opaque Quand la révocation immédiate est prioritaire Contrôle centralisé via introspection Dépendance à l’autorisation server pour chaque vérification
Session Applications web classiques, back-office, monolithe ou front serveur Simplicité d’exploitation et de révocation Moins naturel pour les APIs distribuées et les clients hétérogènes

Mon arbitrage est assez stable: JWT pour une API moderne consommée par plusieurs clients ou services, opaque token quand la révocation doit rester centrale et immédiate, session quand la simplicité prime sur la distribution. Pour un projet d’entreprise, je regarde aussi le coût opérationnel: surveillance des clés, stratégie de rotation, redémarrages, découpage des responsabilités entre équipe API et équipe identité. C’est souvent là que se gagne ou se perd la robustesse réelle du système.

Ce que je verrouillerais avant de passer l’API en production

Si je devais résumer la mise en production en quelques vérifications concrètes, je commencerais par celles-ci:

  • valider l’émetteur avec issuer-uri et l’audience avec audiences;
  • réduire la durée de vie du jeton d’accès au strict nécessaire;
  • tester un token expiré, un token mal signé et un token avec mauvaise audience;
  • documenter le mapping entre claims et autorités dans l’équipe;
  • masquer les jetons dans les logs et les traces applicatives;
  • vérifier la rotation des clés avant un changement d’environnement ou de fournisseur d’identité;
  • garder une stratégie claire pour les cas multi-tenant si plusieurs émetteurs existent.

En pratique, je cherche toujours la même combinaison: un émetteur unique ou maîtrisé, une audience explicite, des jetons courts, un mapping de claims lisible et un refus net de tout ce qui sort du cadre. C’est cette discipline qui rend l’authentification par jetons avec Spring Security fiable, maintenable et compréhensible pour une équipe produit comme pour une équipe sécurité.

Questions fréquentes

Un JWT (JSON Web Token) est un jeton signé qui garantit l'intégrité et l'authenticité des informations. Spring Security l'utilise comme "resource server" pour valider un "Bearer token" envoyé par le client, vérifiant sa signature et ses claims avant d'autoriser l'accès aux ressources.
Une durée de vie courte (5 à 15 minutes) réduit la fenêtre d'exploitation en cas de compromission du jeton. Un jeton plus long augmente le risque de réutilisation non autorisée. Un mécanisme de renouvellement séparé est préférable pour maintenir la sécurité et l'expérience utilisateur.
Il est crucial de valider l'émetteur (iss), l'audience (aud), l'expiration (exp) et la validité avant (nbf). Ces claims assurent que le jeton provient d'une source de confiance, est destiné à votre service et est utilisé dans sa période de validité.
Spring Security permet de mapper les claims du JWT (comme les scopes ou rôles) en "authorities" via un `JwtAuthenticationConverter`. Cela rend les permissions exploitables par `@PreAuthorize` ou les règles d'autorisation HTTP, séparant l'identité des droits d'accès.
Choisissez JWT pour les APIs distribuées nécessitant une validation locale rapide. Un jeton opaque est préférable pour une révocation immédiate centralisée. Les sessions conviennent aux applications web monolithiques où la simplicité d'exploitation est prioritaire.

Évaluer l'article

Moyenne: 0.0 / 5 · 0 évaluations

Tags

spring security jwt authentification jwt spring security configuration spring security jwt claims
Autor Rémy Bonneau
Rémy Bonneau
Je suis Rémy Bonneau, un analyste de l'industrie passionné par la gestion des technologies de l'information, les projets et la transformation numérique. Fort de plusieurs années d'expérience dans l'analyse des tendances du marché, j'ai acquis une expertise approfondie dans la mise en œuvre de stratégies efficaces qui favorisent la réussite des projets technologiques. Mon approche consiste à simplifier des données complexes pour rendre l'information accessible et pertinente. Je m'engage à fournir des analyses objectives et à jour, en mettant l'accent sur la véracité des faits. Mon objectif est d'accompagner les professionnels et les entreprises dans leur parcours de transformation, en leur offrant des insights précieux pour naviguer dans un environnement en constante évolution. Je suis convaincu que la transparence et la rigueur sont essentielles pour établir la confiance avec mes lecteurs. C'est pourquoi je m'efforce de partager des informations fiables et pertinentes, contribuant ainsi à leur succès dans le domaine de la gestion IT et des projets de transformation.

Commentaires (0)

Ajouter un commentaire