JwtTokenService.java
package fr.tiogars.domaintemplate.domains.auth.services;
import java.nio.charset.StandardCharsets;
import java.time.Clock;
import java.time.Instant;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import javax.crypto.SecretKey;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;
import fr.tiogars.domaintemplate.config.JwtProperties;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
/** Generates and validates signed JWT access tokens. */
@Service
public class JwtTokenService {
private final Clock clock;
private final JwtProperties properties;
@Autowired
public JwtTokenService(JwtProperties properties) {
this(properties, Clock.systemUTC());
}
JwtTokenService(JwtProperties properties, Clock clock) {
this.properties = properties;
this.clock = clock;
}
public String generateToken(UserDetails userDetails) {
Instant now = Instant.now(clock);
List<String> authorities = userDetails.getAuthorities().stream()
.map(authority -> authority.getAuthority())
.toList();
return Jwts.builder()
.subject(userDetails.getUsername())
.issuedAt(Date.from(now))
.expiration(Date.from(expiresAt(now)))
.claim("authorities", authorities)
.signWith(signingKey())
.compact();
}
public Optional<String> extractUsername(String token) {
try {
return Optional.ofNullable(Jwts.parser()
.verifyWith(signingKey())
.build()
.parseSignedClaims(token)
.getPayload()
.getSubject());
} catch (JwtException | IllegalArgumentException _) {
return Optional.empty();
}
}
public boolean isValid(String token, UserDetails userDetails) {
return extractUsername(token)
.map(username -> username.equals(userDetails.getUsername()))
.orElse(false);
}
public Instant expiresAt() {
return expiresAt(Instant.now(clock));
}
private Instant expiresAt(Instant now) {
return now.plusSeconds(properties.expirationSeconds());
}
private SecretKey signingKey() {
return Keys.hmacShaKeyFor(properties.secret().getBytes(StandardCharsets.UTF_8));
}
}