128 lines
3.9 KiB
Java
128 lines
3.9 KiB
Java
package com.lions.dev.service;
|
|
|
|
import com.lions.dev.entity.users.Users;
|
|
import com.lions.dev.util.UserRoles;
|
|
import io.smallrye.jwt.build.Jwt;
|
|
import jakarta.enterprise.context.ApplicationScoped;
|
|
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
|
import org.jboss.logging.Logger;
|
|
|
|
import javax.crypto.SecretKey;
|
|
import javax.crypto.spec.SecretKeySpec;
|
|
import java.nio.charset.StandardCharsets;
|
|
import java.time.Duration;
|
|
import java.util.HashSet;
|
|
import java.util.Set;
|
|
|
|
/**
|
|
* Service d'émission de JWT au login.
|
|
*
|
|
* Le token contient :
|
|
* - sub (subject) : userId de l'utilisateur
|
|
* - groups : rôles de l'utilisateur (pour @RolesAllowed)
|
|
* - iss (issuer) : "afterwork"
|
|
* - iat (issued at) : timestamp de création
|
|
* - exp (expiration) : timestamp d'expiration
|
|
*
|
|
* La validation des tokens est assurée automatiquement par SmallRye JWT
|
|
* lorsque les endpoints sont protégés avec @RolesAllowed.
|
|
*
|
|
* @since 2.0 - Implémentation sécurité JWT production-ready
|
|
*/
|
|
@ApplicationScoped
|
|
public class JwtService {
|
|
|
|
private static final Logger LOG = Logger.getLogger(JwtService.class);
|
|
private static final String ISSUER = "afterwork";
|
|
|
|
@ConfigProperty(name = "afterwork.jwt.secret", defaultValue = "afterwork-jwt-secret-min-32-bytes-for-hs256!")
|
|
String secret;
|
|
|
|
@ConfigProperty(name = "smallrye.jwt.new-token.lifespan", defaultValue = "86400")
|
|
long tokenLifespanSeconds;
|
|
|
|
/**
|
|
* Génère un JWT pour l'utilisateur authentifié.
|
|
*
|
|
* @param user L'utilisateur après login réussi
|
|
* @return Le token JWT signé (à envoyer au client dans la réponse d'authentification)
|
|
*/
|
|
public String generateToken(Users user) {
|
|
if (user == null || user.getId() == null) {
|
|
throw new IllegalArgumentException("User et id obligatoires pour générer le JWT");
|
|
}
|
|
|
|
// Construire les groupes (rôles) pour @RolesAllowed
|
|
Set<String> groups = buildGroups(user);
|
|
|
|
SecretKey key = secretKeyFromConfig();
|
|
|
|
String token = Jwt.claims()
|
|
.issuer(ISSUER)
|
|
.subject(user.getId().toString())
|
|
.groups(groups)
|
|
.issuedAt(java.time.Instant.now())
|
|
.expiresIn(Duration.ofSeconds(tokenLifespanSeconds))
|
|
.jws()
|
|
.algorithm(io.smallrye.jwt.algorithm.SignatureAlgorithm.HS256)
|
|
.sign(key);
|
|
|
|
LOG.info("JWT généré pour l'utilisateur " + user.getId() + " avec rôles: " + groups);
|
|
return token;
|
|
}
|
|
|
|
/**
|
|
* Construit l'ensemble des groupes (rôles) pour le token JWT.
|
|
* Inclut le rôle principal et les rôles implicites selon la hiérarchie.
|
|
*
|
|
* Hiérarchie : SUPER_ADMIN > ADMIN > MANAGER > USER
|
|
*
|
|
* @param user L'utilisateur
|
|
* @return Set des groupes à inclure dans le token
|
|
*/
|
|
private Set<String> buildGroups(Users user) {
|
|
Set<String> groups = new HashSet<>();
|
|
String role = user.getRole() != null ? user.getRole().toUpperCase() : UserRoles.USER;
|
|
|
|
// Ajouter le rôle principal
|
|
groups.add(role);
|
|
|
|
// Ajouter les rôles implicites selon la hiérarchie
|
|
switch (role) {
|
|
case UserRoles.SUPER_ADMIN:
|
|
groups.add(UserRoles.ADMIN);
|
|
groups.add(UserRoles.MANAGER);
|
|
groups.add(UserRoles.USER);
|
|
break;
|
|
case UserRoles.ADMIN:
|
|
groups.add(UserRoles.MANAGER);
|
|
groups.add(UserRoles.USER);
|
|
break;
|
|
case UserRoles.MANAGER:
|
|
groups.add(UserRoles.USER);
|
|
break;
|
|
case UserRoles.USER:
|
|
default:
|
|
// USER n'a pas de rôles implicites supplémentaires
|
|
break;
|
|
}
|
|
|
|
return groups;
|
|
}
|
|
|
|
/**
|
|
* Crée la clé secrète à partir de la configuration.
|
|
* Assure que la clé fait au moins 32 bytes pour HS256.
|
|
*/
|
|
private SecretKey secretKeyFromConfig() {
|
|
byte[] decoded = secret.getBytes(StandardCharsets.UTF_8);
|
|
if (decoded.length < 32) {
|
|
byte[] padded = new byte[32];
|
|
System.arraycopy(decoded, 0, padded, 0, decoded.length);
|
|
decoded = padded;
|
|
}
|
|
return new SecretKeySpec(decoded, "HmacSHA256");
|
|
}
|
|
}
|
|
|