JwtService.java
package com.order.api01authgateway.security;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;
import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
/**
* Service component responsible for handling JWT (JSON Web Token) lifecycle management.
* <p>
* This service provides capabilities for generating tokens, extracting claims, and validating
* tokens against {@link UserDetails}. It uses the HS256 algorithm for signing tokens.
* </p>
*
* @see JwtAuthenticationFilter
*/
@Service
public class JwtService {
@Value("${application.security.jwt.secret-key}")
private String secretKey;
@Value("${application.security.jwt.expiration}")
private long jwtExpiration;
/**
* Extracts the subject (username) from the provided JWT.
*
* @param token The JWT string to parse.
* @return The username contained within the token subject.
*/
public String extractUsername(String token) {
return extractClaim(token, Claims::getSubject);
}
/**
* Extracts a specific claim from the JWT using the provided resolver function.
*
* @param token The JWT string to parse.
* @param claimsResolver Function to resolve the desired claim.
* @param <T> The type of the claim.
* @return The resolved claim value.
*/
public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
final Claims claims = extractAllClaims(token);
return claimsResolver.apply(claims);
}
/**
* Generates a new JWT for the specified {@link UserDetails} using default configuration.
*
* @param userDetails The user details to generate the token for.
* @return A signed JWT string.
*/
public String generateToken(UserDetails userDetails) {
return generateToken(new HashMap<>(), userDetails);
}
/**
* Generates a new JWT including extra claims for the specified {@link UserDetails}.
*
* @param extraClaims Additional claims to include in the token payload.
* @param userDetails The user details to generate the token for.
* @return A signed JWT string.
*/
public String generateToken(Map<String, Object> extraClaims, UserDetails userDetails) {
return buildToken(extraClaims, userDetails, jwtExpiration);
}
/**
* Retrieves the configured JWT expiration duration in milliseconds.
*
* @return The expiration duration.
*/
public long getExpirationTime() {
return jwtExpiration;
}
private String buildToken(
Map<String, Object> extraClaims,
UserDetails userDetails,
long expiration
) {
return Jwts
.builder()
.claims(extraClaims)
.subject(userDetails.getUsername())
.issuedAt(new Date(System.currentTimeMillis()))
.expiration(new Date(System.currentTimeMillis() + expiration))
.signWith(getSignInKey(), Jwts.SIG.HS256)
.compact();
}
/**
* Validates the provided JWT against the {@link UserDetails}.
*
* @param token The JWT to validate.
* @param userDetails The {@link UserDetails} to match the token subject against.
* @return {@code true} if the token is valid and not expired, {@code false} otherwise.
*/
public boolean isTokenValid(String token, UserDetails userDetails) {
final String username = extractUsername(token);
return (username.equals(userDetails.getUsername())) && !isTokenExpired(token);
}
private boolean isTokenExpired(String token) {
return extractExpiration(token).before(new Date());
}
private Date extractExpiration(String token) {
return extractClaim(token, Claims::getExpiration);
}
private Claims extractAllClaims(String token) {
return Jwts
.parser()
.verifyWith(getSignInKey())
.build()
.parseSignedClaims(token)
.getPayload();
}
private SecretKey getSignInKey() {
byte[] keyBytes = secretKey.getBytes(StandardCharsets.UTF_8);
return Keys.hmacShaKeyFor(keyBytes);
}
}