Le JSON Web Token (JWT) est une norme ouverte (RFC 7519) qui définit un moyen compact et autnoome de transmettre en toute sécurité des informations entre les parties sous forme d'objet JSON. Dans une architecture moderne, il est couramment utilisé pour l'authentification et l'échange d'informations.
1. Structure technique d'un jeton JWT
Un JWT se compose de trois segments distincts séparés par des points (.) :
- L'en-tête (Header) : Contient généralement le type de jeton (JWT) et l'algorithme de hachage utilisé (ex: HS256).
- La charge utile (Payload) : Contient les "claims" (revendications). On y trouve trois catégories :
- Revendications enrgeistrées : Des champs prédéfinis comme
iss(émetteur),exp(expiration),sub(sujet), etjti(identifiant unique). - Revendications publiques : Définies par les utilisateurs de JWT, elles doivent éviter les collisions.
- Revendications privées : Informations personnalisées partagées entre les parties (ex: rôles utilisateur).
- Revendications enrgeistrées : Des champs prédéfinis comme
- La signature : Créée en combinant l'en-tête encodé, la charge utile encodée, une clé secrète et l'algorithme spécifié.
2. Intégration de la bibliothèque JJWT
Pour manipuler les JWT en Java, la bibliothèque JJWT (Java JWT) est une solution de référence. Voici comment l'ajouter via Maven :
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
3. Exemples de manipulation de jetons
Voici comment générer et analyser un jeton incluant des données personnalisées et une gestion d'expiration.
// Génération d'un jeton avec des données personnalisées
public String genererJeton(String idUtilisateur, String pseudonyme) {
long tempsActuel = System.currentTimeMillis();
long expiration = tempsActuel + (60 * 60 * 1000); // 1 heure
return Jwts.builder()
.setId(idUtilisateur)
.setSubject(pseudonyme)
.setIssuedAt(new Date(tempsActuel))
.setExpiration(new Date(expiration))
.claim("role", "MODERATOR")
.claim("service", "API_V1")
.signWith(SignatureAlgorithm.HS256, "maCleSecreteSuperSecurisee")
.compact();
}
// Analyse et validation d'un jeton
public void validerEtExtraire(String jeton) {
try {
Claims corps = Jwts.parser()
.setSigningKey("maCleSecreteSuperSecurisee")
.parseClaimsJws(jeton)
.getBody();
System.out.println("ID: " + corps.getId());
System.out.println("Sujet: " + corps.getSubject());
System.out.println("Rôle: " + corps.get("role"));
} catch (ExpiredJwtException e) {
System.err.println("Le jeton a expiré.");
}
}
4. Intégration dans un projet Spring Boot
Pour automatiser la sécurité, nous configurons une classe utilitaire et un intercepteur de requêtes.
Configuration (application.yml)
auth:
jwt:
secret: cle_tres_longue_et_securisee_pour_le_hachage
expiration-ms: 86400000 # 24 heures
Composant de gestion des jetons
@Component
@ConfigurationProperties(prefix = "auth.jwt")
public class JwtManager {
private String secret;
private long expirationMs;
// Getters et Setters nécessaires pour @ConfigurationProperties
public String createToken(String userId, String info) {
return Jwts.builder()
.setId(userId)
.setSubject(info)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + expirationMs))
.signWith(SignatureAlgorithm.HS256, secret)
.compact();
}
public Claims decodeToken(String token) {
return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
}
}
Intercepteur de sécurité
L'intercepteur vérifie la présence du jeton dans l'en-tête Authorization pour chaque requête entrante.
@Component
public class AuthInterceptor extends HandlerInterceptorAdapter {
@Autowired
private JwtManager jwtManager;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String header = request.getHeader("Authorization");
if (header != null && header.startsWith("Bearer ")) {
String jeton = header.substring(7);
try {
Claims donnees = jwtManager.decodeToken(jeton);
request.setAttribute("user_data", donnees);
} catch (Exception e) {
// Log de l'erreur ou rejet de la requête
}
}
return true;
}
}
Enregistrement du mécanisme
Enfin, nous déclarons l'intercepteur dans la configuration MVC pour protéger les points d'entrée de l'API.
@Configuration
public class WebSecurityConfig extends WebMvcConfigurationSupport {
@Autowired
private AuthInterceptor authInterceptor;
@Override
protected void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authInterceptor)
.addPathPatterns("/api/**")
.excludePathPatterns("/api/auth/login", "/api/public/**");
}
}
Dans vos contrôleurs, il suffit ensuite de vérifier la présence de l'attribut user_data pour valider les droits d'accès de l'utiliasteur.