1
1
package com .wooteco .wiki .domain ;
2
2
3
3
import com .wooteco .wiki .exception .WrongTokenException ;
4
+ import io .jsonwebtoken .Claims ;
5
+ import io .jsonwebtoken .Jws ;
6
+ import io .jsonwebtoken .JwtException ;
7
+ import io .jsonwebtoken .Jwts ;
8
+ import io .jsonwebtoken .security .Keys ;
9
+ import java .time .Instant ;
10
+ import java .time .LocalDateTime ;
11
+ import java .time .ZoneId ;
12
+ import java .util .Date ;
4
13
import org .springframework .beans .factory .annotation .Value ;
5
14
import org .springframework .stereotype .Component ;
6
15
7
16
@ Component
8
17
public class TokenManager {
18
+ private static final String MEMBER_ID = "member_id" ;
19
+ private static final String TOKEN_TYPE = "token_type" ;
20
+ private static final long ACCESS_TOKEN_LIFE_TIME_AS_HOUR = 1 ;
21
+ private static final long REFRESH_TOKEN_LIFE_TIME_AS_HOUR = 24 ;
9
22
@ Value ("${jwt.key}" )
10
23
private String secretKey ;
11
24
12
25
/**
13
- * 생성된 엑세스 토큰은 리프레쉬 토큰으로 사용할 수 없어야 함. 엑세스 토큰에는 회원의 식별자를 제외한 다른 개인 정보가 포함되면 안됨.
26
+ * 생성된 엑세스 토큰은 리프레쉬 토큰으로 사용할 수 없어야 함. 엑세스 토큰에는 회원의 식별자를 제외한 다른 개인 정보가 포함되면 안됨. 우선 엑세스 토큰 유효 시간은 발급 시점으로부터 1시간으로 설정함.
27
+ * 논의 후 조정하기로!
14
28
*
15
29
* @param member 회원 도메인
16
30
* @return 엑세스 토큰
17
31
*/
18
32
public String generateAccessToken (Member member ) {
19
- return null ;
33
+ LocalDateTime rawExpiredTime = LocalDateTime .now ().plusHours (ACCESS_TOKEN_LIFE_TIME_AS_HOUR );
34
+ Date expiredTime = localDateTimeToDate (rawExpiredTime );
35
+ return Jwts .builder ()
36
+ .expiration (expiredTime )
37
+ .claim (TOKEN_TYPE , "access" )
38
+ .claim (MEMBER_ID , member .getMemberId ())
39
+ .signWith (Keys .hmacShaKeyFor (secretKey .getBytes ()))
40
+ .compact ();
41
+ }
42
+
43
+ private Date localDateTimeToDate (LocalDateTime expiredTime ) {
44
+ Instant instant = expiredTime .atZone (ZoneId .systemDefault ()).toInstant ();
45
+ return Date .from (instant );
20
46
}
21
47
22
48
/**
23
- * 생성된 리프레쉬 토큰은 엑세스 토큰으로 사용할 수 없어야 함.
49
+ * 생성된 리프레쉬 토큰은 엑세스 토큰으로 사용할 수 없어야 함. 우선 리프레쉬 토큰 유효 시간은 발급 시점으로부터 1일로 설정함. 논의 후 조정하기로!
24
50
*
25
51
* @param member 회원 도메인
26
52
* @return 리프레쉬 토큰
27
53
*/
28
54
public String generateRefreshToken (Member member ) {
29
- return null ;
55
+ LocalDateTime rawExpiredTime = LocalDateTime .now ().plusHours (REFRESH_TOKEN_LIFE_TIME_AS_HOUR );
56
+ Date expiredTime = localDateTimeToDate (rawExpiredTime );
57
+ return Jwts .builder ()
58
+ .expiration (expiredTime )
59
+ .claim (TOKEN_TYPE , "refresh" )
60
+ .claim (MEMBER_ID , member .getMemberId ())
61
+ .signWith (Keys .hmacShaKeyFor (secretKey .getBytes ()))
62
+ .compact ();
30
63
}
31
64
32
65
/**
@@ -37,6 +70,24 @@ public String generateRefreshToken(Member member) {
37
70
* @throws WrongTokenException 엑세스 토큰에 문제(잘못된 토큰, 기간 만료 등)가 있어 회원 식별자를 추출할 수 없는 경우
38
71
*/
39
72
public long extractMemberId (String accessToken ) throws WrongTokenException {
40
- return -1 ;
73
+ try {
74
+ Jws <Claims > claimsJws = Jwts .parser ()
75
+ .verifyWith (Keys .hmacShaKeyFor (secretKey .getBytes ()))
76
+ .build ()
77
+ .parseSignedClaims (accessToken );
78
+
79
+ Claims payload = claimsJws .getPayload ();
80
+ validateTokenIsAccessToken (payload );
81
+ return payload .get (MEMBER_ID , Long .class );
82
+ } catch (JwtException | IllegalArgumentException e ) {
83
+ throw new WrongTokenException ("잘못된 토큰입니다." );
84
+ }
85
+ }
86
+
87
+ private static void validateTokenIsAccessToken (Claims payload ) {
88
+ String tokenType = payload .get (TOKEN_TYPE , String .class );
89
+ if (!tokenType .equals ("access" )) {
90
+ throw new WrongTokenException ("엑세스 토큰이 아닙니다." );
91
+ }
41
92
}
42
93
}
0 commit comments