Skip to main content

JWT (version que Filter)

Dépendances

Dans le build.gradle

// Jwt
implementation 'io.jsonwebtoken:jjwt-api:0.12.6'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.6'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.6'

Générer un token

Le code java isolé, natif pour générer un token:

// Pour genere un token
// -- la date ou ca été crée (IssueAt)
// -- une date d'expiration (Expiration)
// -- une donnée subjectif (Subject)
// -- l'algo pour crypter (HS256)
// -- la clé secrête

// -- temps de vie du token
Date tokenLifetime = new Date(System.currentTimeMillis() + (1000 * 60 * 60) * 1);

// Le code qui genere le token
String token = Jwts.builder()
.setSubject("quelquun@gmail.com")
.issuedAt(new Date(System.currentTimeMillis()))
.setExpiration(tokenLifetime)
.signWith(getKey(), SignatureAlgorithm.HS256)
.compact();

Tester un token

Un exemple de code java pour tester un token:

// Récupérer le token dans le header authorization
// On substring 7 caractères car le header contient "Bearer tontoken"
String token = authorization.substring(7);

try {
// Outil pour récupérer le token (déchiffrer)
JwtParser jwtParser = Jwts.parser().setSigningKey(key).build();

// -- récupérer les claims de mon token (claims => toutes les info)
Claims claims = jwtParser.parseSignedClaims(token).getBody();

// Récupérer la date
// 1: Version abstraite
// Function<Claims, Date> expirationFunction = Claims::getExpiration;
// Date expirationDate = expirationFunction.apply(claims);
// 2: Version explicite
Date expirationDate = claims.getExpiration();

} catch (Exception e) {
// Si la date d'expiration est depassé alors
// Si exception jwt de type expiration
if (e instanceof ExpiredJwtException){
return "Token expiré";
}

// Si token malformé
if (e instanceof MalformedJwtException){
return "Token malformé";
}

return "Erreur inconnue";
}

return "Token valide";

Externaliser l'accès à la clé secrète

Voici un exemple pour déporter l'accès à la clé secrète avec la valeur de la clé stocker dans un fichier config

/**
* Récupérer la valeur de app.jwt.secret dans application.properties
*/
@Value("${app.jwt.secret}")
private String SECRET_KEY;

private Key getSecretKey() {
// convertir un string en base 64
byte[] keyBytes = Decoders.BASE64.decode(SECRET_KEY);
// convertir une base 64 en Key
Key key = Keys.hmacShaKeyFor(keyBytes);

return key;
}

Evidement cela veut dire que dans votre application.properties (dans notre cas en tout cas) vous avez bien :

app.jwt.secret=69636e783529213d5722613b2b336c793371666524684a3445226e5573

Filter (Middleware)

Filter c'est un middleware (comme un pare-feu) qui permet de faire du traitement entre chaque requête http

On avait un Filter nommé :

public class JwtAuthFilter extends OncePerRequestFilter

Rappel interpréter chaque requête dans le Filter, surcharger :

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

Exemple pour répondre à ces besoins :

  • Verifier qu'on a envoyer un token valide quand on est en dehors de l'url api/create-token
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
// Si l'url est different de /api/create-token -> verifier le token
String url = request.getRequestURI();

if (!url.startsWith("/api/create-token")) {
// Récupérer le token
String token = request.getHeader("Authorization");

// Appeler notre service qui check le token
ServiceResponse<Boolean> serviceResponse = authService.checkToken(token);

// Si pas bon (!= 202 code métier)
if (!serviceResponse.code.equals("202")) {
// Forcer la réponse http à être en JSON
response.setContentType("application/json");

objectMapper.writeValue(response.getWriter(), serviceResponse);
return;
}
}

// passer (Oui/Ok)
filterChain.doFilter(request, response);
}

Passer le middleware (donc ok) :

filterChain.doFilter(request, response);

On ne passe pas le middleware (donc pas ok) :

return

Attention dans le cas ou on ne passe pas, il est recommandé d'avoir une réponse http pertinente :

// Forcer la réponse http à être en JSON
response.setContentType("application/json");

// Ecrire dans la réponse http (dans notre cas du métier)
objectMapper.writeValue(response.getWriter(), serviceResponse);

Utiliser/Activer notre Filter

Il faut injecter votre filter dans votre config Spring security, example :

@Configuration
@EnableWebSecurity
public class SpringSecurityConfig {

private final JwtAuthFilter jwtAuthFilter;

public SpringSecurityConfig(JwtAuthFilter jwtAuthFilter) {
this.jwtAuthFilter = jwtAuthFilter;
}
...