Skip to content

Commit 748738b

Browse files
committed
feat: JwtUtil 구현
- 토큰 생성, 저장, Claims 추출, 만료 등의 작업 수행
1 parent a33352e commit 748738b

File tree

1 file changed

+133
-0
lines changed

1 file changed

+133
-0
lines changed
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
package dmu.dasom.api.global.auth.jwt;
2+
3+
import dmu.dasom.api.domain.common.exception.CustomException;
4+
import dmu.dasom.api.domain.common.exception.ErrorCode;
5+
import dmu.dasom.api.global.auth.dto.TokenBox;
6+
import io.jsonwebtoken.*;
7+
import io.jsonwebtoken.security.SignatureException;
8+
import org.springframework.beans.factory.annotation.Value;
9+
import org.springframework.data.redis.core.StringRedisTemplate;
10+
import org.springframework.stereotype.Component;
11+
12+
import javax.crypto.SecretKey;
13+
import javax.crypto.spec.SecretKeySpec;
14+
import java.nio.charset.StandardCharsets;
15+
import java.util.Date;
16+
import java.util.concurrent.TimeUnit;
17+
18+
@Component
19+
public class JwtUtil {
20+
21+
@Value("${jwt.access-token-expiration}")
22+
private long accessTokenExpiration;
23+
24+
@Value("${jwt.refresh-token-expiration}")
25+
private long refreshTokenExpiration;
26+
27+
private static final String ACCESS_TOKEN_PREFIX = "ACCESS_";
28+
private static final String REFRESH_TOKEN_PREFIX = "REFRESH_";
29+
private static final String BLACKLIST_PREFIX = "BLACKLIST_";
30+
31+
private final SecretKey secretKey;
32+
private final StringRedisTemplate redisTemplate;
33+
34+
public JwtUtil(@Value("${jwt.secret}") final String secretKey, final StringRedisTemplate redisTemplate) {
35+
this.secretKey = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), Jwts.SIG.HS256.key().build().getAlgorithm());
36+
this.redisTemplate = redisTemplate;
37+
}
38+
39+
// Access, Refresh 토큰 생성
40+
public TokenBox generateTokenBox(final String email) {
41+
final TokenBox tokenBox = TokenBox.builder()
42+
.accessToken(generateToken(email, accessTokenExpiration))
43+
.refreshToken(generateToken(email, refreshTokenExpiration))
44+
.build();
45+
46+
saveTokens(tokenBox, email);
47+
48+
return tokenBox;
49+
}
50+
51+
// 토큰 생성
52+
public String generateToken(final String email, final long expirationMs) {
53+
return Jwts.builder()
54+
.issuer("dmu-dasom.or.kr")
55+
.subject(email)
56+
.issuedAt(new Date(System.currentTimeMillis()))
57+
.expiration(new Date(System.currentTimeMillis() + expirationMs))
58+
.signWith(this.secretKey)
59+
.compact();
60+
}
61+
62+
// 토큰 저장
63+
public void saveTokens(final TokenBox tokenBox, final String email) {
64+
redisTemplate.opsForValue().set(ACCESS_TOKEN_PREFIX.concat(email), tokenBox.getAccessToken(), accessTokenExpiration, TimeUnit.MILLISECONDS);
65+
redisTemplate.opsForValue().set(REFRESH_TOKEN_PREFIX.concat(email), tokenBox.getRefreshToken(), refreshTokenExpiration, TimeUnit.MILLISECONDS);
66+
}
67+
68+
// Request 헤더에서 토큰 추출
69+
public String parseToken(final String authHeaderValue) {
70+
return authHeaderValue.split(" ")[1];
71+
}
72+
73+
// 토큰으로부터 Claims 추출
74+
public Claims parseClaims(final String token) {
75+
try {
76+
return Jwts.parser()
77+
.verifyWith(this.secretKey)
78+
.build()
79+
.parseSignedClaims(token)
80+
.getPayload();
81+
} catch (SecurityException | MalformedJwtException | SignatureException |
82+
UnsupportedJwtException | IllegalArgumentException | ClaimJwtException e) {
83+
throw new CustomException(ErrorCode.TOKEN_NOT_VALID);
84+
}
85+
}
86+
87+
// 토큰의 남은 수명 반환
88+
public long getRemainingTokenExpiration(final String token) {
89+
return parseClaims(token).getExpiration().getTime() - System.currentTimeMillis();
90+
}
91+
92+
// Access, Refresh 토큰 블랙리스트 추가
93+
public void blacklistTokens(final String email) {
94+
final String accessTokenKey = ACCESS_TOKEN_PREFIX.concat(email);
95+
final String refreshTokenKey = REFRESH_TOKEN_PREFIX.concat(email);
96+
97+
if (redisTemplate.hasKey(accessTokenKey)) {
98+
blacklistToken(redisTemplate.opsForValue().get(accessTokenKey));
99+
redisTemplate.delete(accessTokenKey);
100+
}
101+
102+
if (redisTemplate.hasKey(refreshTokenKey)) {
103+
blacklistToken(redisTemplate.opsForValue().get(refreshTokenKey));
104+
redisTemplate.delete(refreshTokenKey);
105+
}
106+
}
107+
108+
// 토큰 블랙리스트 추가
109+
public void blacklistToken(final String token) {
110+
redisTemplate.opsForValue().set(BLACKLIST_PREFIX.concat(token), token, getRemainingTokenExpiration(token));
111+
}
112+
113+
// 토큰 블랙리스트 확인
114+
public boolean isBlacklisted(final String token) {
115+
return redisTemplate.hasKey(BLACKLIST_PREFIX.concat(token));
116+
}
117+
118+
// 토큰 만료 여부 확인
119+
public boolean isExpired(final String token) {
120+
try {
121+
return Jwts.parser()
122+
.verifyWith(this.secretKey)
123+
.build()
124+
.parseSignedClaims(token)
125+
.getPayload()
126+
.getExpiration()
127+
.before(new Date());
128+
} catch (ExpiredJwtException ex) {
129+
return true;
130+
}
131+
}
132+
133+
}

0 commit comments

Comments
 (0)