JwtUtil.java

package qwerty.chaekit.global.jwt;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import jakarta.annotation.Nullable;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import qwerty.chaekit.domain.member.Member;
import qwerty.chaekit.domain.member.publisher.PublisherProfile;
import qwerty.chaekit.domain.member.user.UserProfile;
import qwerty.chaekit.global.properties.JwtProperties;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.function.UnaryOperator;

@Slf4j
@Component
public class JwtUtil {
    private final SecretKey secretKey;
    private final JwtProperties jwtProperties;

    public JwtUtil(JwtProperties jwtProperties) {
        this.jwtProperties = jwtProperties;
        this.secretKey = new SecretKeySpec(
                jwtProperties.secret().getBytes(StandardCharsets.UTF_8),
                Jwts.SIG.HS256.key().build().getAlgorithm()
        );
    }

    public TokenParsingResult parseAccessToken(String token) {
        return parseToken(token, "access");
    }

    public TokenParsingResult parseRefreshToken(String token) {
        return parseToken(token, "refresh");
    }

    public TokenParsingResult parseToken(String token, String expectedType) {
        try {
            Claims claims = Jwts.parser()
                    .verifyWith(secretKey)
                    .build()
                    .parseSignedClaims(token)
                    .getPayload();

            if (!claims.get("type", String.class).equals(expectedType)) {
                return TokenParsingResult.of(TokenStatus.INVALID);
            }

            return TokenParsingResult.of(TokenStatus.VALID, claims);

        } catch (ExpiredJwtException e) {
            return TokenParsingResult.of(TokenStatus.EXPIRED);
        } catch (Exception e) {
            return TokenParsingResult.of(TokenStatus.INVALID);
        }
    }
    public String createAccessToken(
            Member member,
            @Nullable UserProfile user,
            @Nullable PublisherProfile publisher
    ) {
        Long userId = user != null ? user.getId() : null;
        Long publisherId = publisher != null ? publisher.getId() : null;
        return createAccessToken(member.getId(), userId, publisherId, member.getEmail(), member.getRole().name());
    }

    public String createAccessToken(Long memberId, Long userId, Long publisherId, String email, String role) {
        return createToken(
                builder -> builder
                        .claim("type", "access")
                        .claim("memberId", memberId)
                        .claim("userId", userId)
                        .claim("publisherId", publisherId)
                        .claim("email", email)
                        .claim("role", role),
                jwtProperties.expirationMs()
        );
    }

    public String createRefreshToken(Long memberId) {
        return createToken(
                builder -> builder
                        .claim("type", "refresh")
                        .claim("memberId", memberId),
                jwtProperties.refreshExpirationMs()
        );
    }

    private String createToken(UnaryOperator<JwtBuilder> claimSetter, long expirationMs) {
        JwtBuilder builder = Jwts.builder();
        builder = claimSetter.apply(builder);
        return builder
                .issuedAt(new Date(System.currentTimeMillis()))
                .expiration(new Date(System.currentTimeMillis() + expirationMs))
                .signWith(secretKey)
                .compact();
    }

}