JwtTokenManager.java
package com.ecommerce.identity.infrastructure.security;
import com.ecommerce.identity.application.port.TokenManager;
import org.springframework.security.oauth2.jose.jws.MacAlgorithm;
import org.springframework.security.oauth2.jwt.JwtClaimsSet;
import org.springframework.security.oauth2.jwt.JwtEncoder;
import org.springframework.security.oauth2.jwt.JwtEncoderParameters;
import org.springframework.security.oauth2.jwt.JwsHeader;
import org.springframework.stereotype.Component;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.time.Instant;
import java.util.Base64;
import java.util.HexFormat;
import java.util.List;
import java.util.UUID;
@Component
public class JwtTokenManager implements TokenManager {
private final JwtEncoder jwtEncoder;
private final TokenProperties properties;
private final SecureRandom secureRandom = new SecureRandom();
public JwtTokenManager(JwtEncoder jwtEncoder, TokenProperties properties) {
this.jwtEncoder = jwtEncoder;
this.properties = properties;
}
@Override
public AccessToken createAccessToken(Long userId, List<String> roleCodes, Instant now) {
Instant expiresAt = now.plus(properties.accessTtl());
JwtClaimsSet claims = JwtClaimsSet.builder()
.issuer(properties.issuer())
.subject(userId.toString())
.issuedAt(now)
.expiresAt(expiresAt)
.id(UUID.randomUUID().toString())
.claim("roles", List.copyOf(roleCodes))
.build();
JwsHeader header = JwsHeader.with(MacAlgorithm.HS256).build();
String value = jwtEncoder.encode(JwtEncoderParameters.from(header, claims)).getTokenValue();
return new AccessToken(value, properties.accessTtl().toSeconds());
}
@Override
public RefreshToken createRefreshToken(Instant now) {
byte[] bytes = new byte[32];
secureRandom.nextBytes(bytes);
String value = Base64.getUrlEncoder().withoutPadding().encodeToString(bytes);
return new RefreshToken(value, hashRefreshToken(value), now.plus(properties.refreshTtl()));
}
@Override
public String hashRefreshToken(String rawToken) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
return HexFormat.of().formatHex(digest.digest(rawToken.getBytes(java.nio.charset.StandardCharsets.UTF_8)));
} catch (NoSuchAlgorithmException exception) {
throw new IllegalStateException("SHA-256 is not available", exception);
}
}
}