서버에서 인증된 사용자가 인증을 유지해주는 방법으로 보통은 세션을 사용함
-> 서버 세션을 사용하면 인증된 사용자는 매우 편리하게 서비스를 이용할 수 있고, 대부분의 웹애플리케이션 서버가 세션을 지원하기 때문에 편리
하지만 세션을 이용할 때
1. 서버를 여러대 둘 경우 (scale out),
2. 같은 사용자가 서로 다른 도메인의 데이터를 요청할 경우, (SSO)에는 세션을 유지하기 위한 비용이 매우 커짐
-> 이 때는 서버에 사용자 정보를 저장하는 대신 클라이언트에 사용자 정보를 내려주고,서버는 토큰의 사용자 정보를 모든 요청에서 확인하고 서비스를 해주는 방식(sessionless)
일 때, JWT 토큰이 유용하게 사용됩
JWT 토큰의 구조 : header
+ body
+ signature
이론적으로는 토큰을 클라이언트가 관리
-> 하지만, 실제로 서버는 사용자 정보 캐싱이나 토큰의 유효성 평가, 혹은 refresh 토큰 정책을 위해 서버에 토큰을 관리하기도 함
이 경우, 토큰과 사용자 정보를 관리하는 방법으로 다음과 같은 방법들을 사용하기도 합니다
public class JWTSimpleTest {
private void printToken(String token){
String[] tokens = token.split("\\.");
// 헤더 및 바디 프린트
System.out.println("header : " + new String(Base64.getDecoder().decode(tokens[0])));
System.out.println("Body : " + new String(Base64.getDecoder().decode(tokens[1])));
}
@DisplayName("1. jjwt 를 이용한 토큰 테스트")
@Test
void test_1(){
String okta_token = Jwts.builder().addClaims(
Map.of("name", "wookjong", "price", 3000)
).signWith(SignatureAlgorithm.HS256, "wookjong")
.compact();
System.out.println(okta_token);
printToken(okta_token);
// 검증
Jws<Claims> tokeninfo = Jwts.parser().setSigningKey("wookjong").parseClaimsJws(okta_token);
System.out.println(tokeninfo);
}
@DisplayName("2. java-jwt 를 이용한 토큰 테스트")
@Test
void test_2() {
// 같은 키를 사용해서 검증하게 하는 법
// 클라이언트가 누구든지 간에 알고리즘과 키값이 서로 일치 한다면 누구든 검증가능한 토큰임
byte[] SEC_KEY = DatatypeConverter.parseBase64Binary("wookjong");
String oauth0_token = JWT.create().withClaim("name", "wookjong").withClaim("price", 3000)
.sign(Algorithm.HMAC256(SEC_KEY));
System.out.println(oauth0_token);
printToken(oauth0_token);
DecodedJWT verified = JWT.require(Algorithm.HMAC256(SEC_KEY)).build().verify(oauth0_token);
System.out.println(verified.getClaims());
// 원래라면 내부적인 key값이 달라 검증 불가
Jws<Claims> tokenInfo = Jwts.parser().setSigningKey(SEC_KEY).parseClaimsJws(oauth0_token);
System.out.println(tokenInfo);
}
@DisplayName("3. 만료 시간 테스트")
@Test
void test3() throws InterruptedException {
final Algorithm AL = Algorithm.HMAC256("wookjong");
// 유효 시간 3초
String token = JWT.create().withSubject("a1234")
.withNotBefore(new Date(System.currentTimeMillis() + 1000))
.withExpiresAt(new Date(System.currentTimeMillis() + 3000))
.sign(AL);
// 이미 만료가 되버림
// Thread.sleep(2000);
try {
DecodedJWT verify = JWT.require(AL).build().verify(token);
System.out.println(verify.getClaims());
} catch(Exception ex){
// 유효하지 않은 경우에도 토큰을 열어보는 경우
System.out.println("유효하지 않은 토큰입니다...");
DecodedJWT decode = JWT.decode(token); // jjwt는 decode 제공 X
System.out.println(decode.getClaims());
}
}
}