JWT를 발급받고 인증해보자!
JWT는 Json Web Token으로, 인증에 필요한 정보들을 토큰에 담아 암호화시켜 사용하는 방식이다. 서명된 토큰이라는 점에서 쿠키보다 더 보안적이라고 할 수 있다.
JWT의 구조
JWT는 Header, Payload, Signature의 구성 요소로 이루어지며 각 구성 요소가 마침표(.)로 구분된다.
Header에는 보통 토큰의 타입이나, 서명 생성에 사용되는 알고리즘을 저장한다.
Payload에는 토큰에 담을 정보를 key-value 형태로 저장한다.
Signature에는 서명이 저장되는데 암호화되어 있기 때문에 서버에 있는 개인 키로만 복호화가 가능하다.
JWT의 구조는 jwt.io 사이트에서 직접 데이터를 암호화하며 확인해볼 수 있다.
JWT를 생성하는 방법은 아주 간단하다.
아래 코드는 JWT 생성 코드로, createJwt 메소드에서 builder 패턴을 이용해 JWT를 생성한다.
// TODO: JWT 생성
public String createJwt(int id){
Date now = new Date();
return Jwts.builder()
.setHeaderParam("type","jwt")
.claim("id", id)
.setIssuedAt(now)
.setExpiration(new Date(System.currentTimeMillis()+1*(1000*60*60*24*365)))
.signWith(SignatureAlgorithm.HS256, SecretKey.JWT_SECRET_KEY)
.compact();
}
비밀 키 예시)
public class SecretKey {
public static String JWT_SECRET_KEY = "236979CB6F1AD6B6A6184A31E6BE37DB3818CC36871E26235DD67DCFE4041492";
}
이제 userIdx를 1로 설정한 뒤 JWT 토큰을 생성하면 다음과 같이 출력될 것이다.
int userIdx = 1;
String jwt = jwtService.createJwt(userIdx);
System.out.println("JWT : " + jwt);
출력 결과 > JWT : eyJ0eXBlIjoiand0IiwiYWxnIjoiSFMyNTYifQ.eyJpZCI6MSwiaWF0IjoxNjUyODcwNjY4LCJleHAiOjE2NTQzNDE4OTd9.5qXqkxjKA48g3s5LouLrfbnYZzs6rvIzSZenMtllULk
JWT 생성을 하였으니 이제 인증을 할 차례이다.
프론트에서 JWT를 "X-ACCESS-TOKEN"이라는 이름의 Header에 담아 넘겨준 상황이라면 아래 코드와 같이 헤더에서 JWT 토큰을 추출할 수 있다.
// TODO: 헤더에서 JWT 추출
public String getJwt(){
HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest();
return request.getHeader("X-ACCESS-TOKEN");
}
위의 getJwt() 메소드로 헤더에서 JWT 토큰을 추출했다면 다음은 JWT 토큰 안의 데이터를 파싱한다.
이때, 토큰 생성에 사용된 비밀키를 이용한다.
claim 파싱이 되면 JWT 토큰 생성에 추가하였던 id 값을 getBody().get() 함수로 추출할 수 있다.
final String INVALID_JWT = "유효하지 않은 토큰입니다.";
final String EMPTY_JWT = "토큰이 비어있습니다.";
// TODO: JWT에서 값 추출
public int getUserId() throws Exception{
// 헤더에서 JWT 추출
String accessToken = getJwt();
if(accessToken == null || accessToken.length() == 0){
throw new Exception(EMPTY_JWT);
}
// JWT 파싱
Jws<Claims> claims;
try{
claims = Jwts.parser()
.setSigningKey(SecretKey.JWT_SECRET_KEY)
.parseClaimsJws(accessToken);
} catch (Exception ignored) {
throw new Exception(INVALID_JWT);
}
// id 추출
return claims.getBody().get("id",Integer.class);
}
사용 예시)
try {
//jwt에서 id 추출.
int userIdByJwt = jwtService.getUserId();
//추출한 ID와 접근한 유저의 ID가 같은지 확인
if(userId != userIdByJwt){
//TODO: 불일치
}
//TODO: 인증 성공 후 진행할 코드 구현
} catch (Exception exception) {
//TODO: 오류 코드 반환
}