JwtTokenProvider.java
package com.deveagles.be15_deveagles_be.common.jwt;
import com.deveagles.be15_deveagles_be.common.exception.BusinessException;
import com.deveagles.be15_deveagles_be.common.exception.ErrorCode;
import com.deveagles.be15_deveagles_be.features.users.command.domain.aggregate.Staff;
import com.deveagles.be15_deveagles_be.features.users.command.repository.UserRepository;
import io.jsonwebtoken.*;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import jakarta.annotation.PostConstruct;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import javax.crypto.SecretKey;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.stereotype.Component;
@Slf4j
@Component
@RequiredArgsConstructor
public class JwtTokenProvider {
@Value("${jwt.secret}")
private String jwtSecret;
@Value("${jwt.expiration}")
private long jwtExpiration;
@Value("${jwt.refresh-expiration}")
private long jwtRefreshExpiration;
private SecretKey secretKey;
private final RedisTemplate<String, String> redisTemplate;
private final UserRepository userRepository;
@PostConstruct
public void init() {
byte[] keyBytes = Decoders.BASE64.decode(jwtSecret);
secretKey = Keys.hmacShaKeyFor(keyBytes);
}
public String createToken(String username) {
Date now = new Date();
Date expiration = new Date(now.getTime() + jwtExpiration);
Staff staff =
userRepository
.findStaffByLoginId(username)
.orElseThrow(() -> new BusinessException(ErrorCode.USER_NAME_NOT_FOUND));
// todo : 라우팅 페이지 별 접근권한 추가
Map<String, String> map = new HashMap<>();
map.put("type", "access");
map.put("shopId", staff.getShopId().toString());
map.put("username", staff.getLoginId());
map.put("staffName", staff.getStaffName());
map.put("userId", staff.getStaffId().toString());
map.put("grade", staff.getGrade());
map.put("profileUrl", staff.getProfileUrl());
map.put("userStatus", staff.getStaffStatus().toString());
map.put("jti", UUID.randomUUID().toString());
return Jwts.builder()
.subject(username)
.issuedAt(now)
.claims(map)
.expiration(expiration)
.signWith(secretKey)
.compact();
}
public String createRefreshToken(String username) {
Date now = new Date();
Date expiration = new Date(now.getTime() + jwtRefreshExpiration);
Staff staff =
userRepository
.findStaffByLoginId(username)
.orElseThrow(() -> new BusinessException(ErrorCode.USER_NAME_NOT_FOUND));
// todo : 라우팅 페이지 별 접근권한 추가
Map<String, String> map = new HashMap<>();
map.put("type", "refresh");
map.put("shopId", staff.getShopId().toString());
map.put("username", staff.getLoginId());
map.put("staffName", staff.getStaffName());
map.put("userId", staff.getStaffId().toString());
map.put("grade", staff.getGrade());
map.put("profileUrl", staff.getProfileUrl());
map.put("userStatus", staff.getStaffStatus().toString());
map.put("jti", UUID.randomUUID().toString());
return Jwts.builder()
.subject(username)
.issuedAt(now)
.claims(map)
.expiration(expiration)
.signWith(secretKey)
.compact();
}
public long getRefreshExpiration() {
return jwtRefreshExpiration;
}
public boolean validateToken(String token) {
try {
Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token);
return true;
} catch (SecurityException | MalformedJwtException e) {
throw new BadCredentialsException("Invalid JWT Token", e);
} catch (ExpiredJwtException e) {
throw new BadCredentialsException("Expired JWT Token", e);
} catch (UnsupportedJwtException e) {
throw new BadCredentialsException("Unsupported JWT Token", e);
} catch (IllegalArgumentException e) {
throw new BadCredentialsException("JWT Token claims empty", e);
}
}
public String getUsernameFromJWT(String token) {
Claims claims =
Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload();
return claims.getSubject();
}
public long getRemainingExpiration(String token) {
Claims claims = parseClaims(token);
return claims.getExpiration().getTime() - System.currentTimeMillis();
}
public Claims parseClaims(String token) {
try {
return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload();
} catch (ExpiredJwtException e) {
return e.getClaims(); // 만료된 토큰도 claims는 꺼낼 수 있음
}
}
public boolean isRefreshToken(String token) {
Claims claims = parseClaims(token);
return "refresh".equals(claims.get("type", String.class));
}
public boolean isAccessTokenBlacklisted(String token) {
return Boolean.TRUE.equals(redisTemplate.hasKey("BL:" + token));
}
}