JWT 가 뭐야??

CHOI·2023년 8월 5일
0

정의

JWT(JSON Web Token)은 웹 어플리케이션과 서버 간의 정보를 안전하게 전달하기 위한 표준 중 하나.

JWT 는 클레임(Claim)을 JSON 객체로 표현하고, JSON 객체를 Base64로 인코딩하여 문자열로 만든 형태로 토큰을 생성한다. 서버에서 여기에 서명하여 인증 정보도 포함하게 된다.

JWT.IO - JSON Web Tokens Introduction

단계

JWT는 크게 3 가지 단계로 구성된다.

  1. 토큰 생성 : 유저가 인증되었을 때, 필요한 정보(클레임)를 JSON 형태로 작성한다. 이 클레임에는 유저 식별 정보나 추가적인 데이터 등을 포함할 수 있다. 이후 해당 클레임을 비밀 서명키를 사용해 서명한다.
  2. 토큰 발급 : 서명된 클레임을 JWT 형식으로 인코딩되어 유저에게 발급된다. 보통 HTTP 요청 헤더에 담아져 전달된다.
  3. 토큰 검증 : 클라이언트가 서버에 요청을 보낼 때, JWT 토큰을 Authorization 헤더에 포함하여 전달한다. 서버는 이 JWT 토큰을 검증하여 클라이언트의 인증 상태를 확인한다. 이때 확인하는 것은 다음과 같다.
    • 토큰의 서명
    • 토큰의 만료
    • 클레임에 담긴 데이터 유효

구조

JWT 는 다음과 같은 구조를 가지고 있다.

Header.Payload.Signature
  • Header : 토큰의 유형과 해시 알고리즘 등의 메타 정보를 포함한다.
  • Payload : 클레임 정보가 들어있다. 예를 들어서 유저 ID, 권한 등의 정보
  • Signature : 서버에서 생성한 서명으로 토큰의 무결성을 보장한다.

헤더는 일반적으로 두 부분으로 구성된다.

  • 토큰의 타입 (JWT)
  • 서명 알고리즘
{
  "alg": "HS256",
  "typ": "JWT"
}

Payload

{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022
}

JWT 의 두번째 부분은 Payload 로 클레임들을 포함하고 있다.

클레임은 JWT 에 포함되어지는 데이터를 말한다. JWT 는 Payload 에 클레임 정보를 JSON 형태로 저장하고 있다. 이 클레임 정보는 서버와 클라이언트 사이에 교환되는 데이터를 말한다.

Payload 에는 3 가지의 클레임이 있다.

  • Registered Claims (등록된 클레임) : JWT 표준 규격에 따라 미리 정의된 클레임들이다. 주요 등록된 클레임은 아래와 같다.
    • "iss" (Issuer): 토큰을 발행한 발행자(서버)의 식별자
    • "sub" (Subject): 토큰에 대한 주제(일반적으로 유저 ID 등)
    • "aud" (Audience): 토큰의 대상자(토큰을 사용할 대상)
    • "exp" (Expiration Time): 토큰의 만료 시간
    • "nbf" (Not Before): 토큰의 사용 가능 시작 시간
    • "iat" (Issued At): 토큰이 발급된 시간
    • "jti" (JWT ID): JWT의 고유 식별자
  • Public Claims (공개된 클래임) : 사용자에 의해 정의되는 클레임이다. 충돌 방지 작업을 해줘야한다.
  • Private Claims (비공개된 클레임): 서버와 클라이언트 사이에 협의된 클레임으로 어떤 데이터든 포함할 수 있다.

public Claims 과 private Claims 의 차이점

둘의 유일한 차이점은 Public Claims 는 충돌방지를 해줘야하지만 Private Claims 은 그럴 필요가 없다는 점이다.

What is difference between private and public claims on jwt

Signature

서명 부분을 생성하려면 인코딩된 헤더, 인코딩된 페이로드, 비밀 키 값(secret), 헤더에 정의한 알고리즘 정보가 필요하다.

예를 들어 HMAC SHA256 알고리즘을 사용하려는 경우 서명은 다음과 같은 방식으로 생성된다.

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

합치기

이렇게 만들어진 인코딩 정보들을 합치면 다음과 같다.

각각의 인코딩 정보들은 . 로 구분된다.

JWT.IO

장점

보안성

서버에서 토큰을 생성하고 유저의 인증 정보를 파악하기 위해 비밀 설정 키를 사용한다. 클라이언트는 이 비밀 설정키를 모르기 때문에 토큰을 변조할 수 없다.

확장성

JWT 를 사용하면 서버에서 세션을 사용해 관리할 필요 없이 클라이언트 측에서 토큰을 저장하고 관리함으로써 서버의 확장성을 높일 수 있다.

JTW 는 표준화가 잘 되어 있어 여러 언어와 플랫폼에서 쉽게 사용할 수 있고 OAuth 와 같은 다른 인증 방식과 잘 통합되어 사용된다.

Java 예제

gradle 추가

implementation group: 'io.jsonwebtoken', name: 'jjwt-api', version: '0.11.2'	
runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-impl', version: '0.11.2'	
runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.11.2'

JWT 생성

private final String SECRETE_KEY = "암호화 키 값";
private static final long EXPIRATION_TIME = 86400000; // 토큰의 만료 시간 (24시간)

public String createJwt(String param) {
		Date now = new Date();
		Date expirationDate = new Date(now.getTime() + EXPIRATION_TIME);

		// 헤더
		Map<String, Object> headers = new HashMap<>();
		headers.put("typ", "JWT");
		headers.put("alg", "HS256");

		// 클레임 설정
		Map<String, Object> claims = new HashMap<>();
		claims.put("param", param);

		Key key = Keys.hmacShaKeyFor(SECRETE_KEY.getBytes());
		String jwt = Jwts.builder()
				.setHeader(headers)
				.setClaims(claims)
				.setIssuedAt(now)
				.setExpiration(expirationDate)
				.signWith(key, SignatureAlgorithm.HS256)
				.compact();
		return jwt;
	}
  • 1000 * 60 * 60 * 10 는 1시간이다.
  • SECRETE_KEY 를 너무 짧게 적으면 아래와 같은 에러가 발생한다.

signWith 을 사용하는 방식이 수정되어서 위와 같이 작성했다.

[Spring Security + Kotlin] Jwts signWith deprecated 오류

JWT 토큰 검증

public Map<String, Object> checkJwt(String jwt) throws UnsupportedEncodingException {
		Map<String, Object> claimMap = null;
		try {
			Claims claims = Jwts.parser()
					.setSigningKey(KEY.getBytes("UTF-8")) // 키 설정
					.parseClaimsJws(jwt) // jwt의 정보를 파싱해서 시그니처 값을 검증한다.
					.getBody();
			claimMap = claims;

		} catch (ExpiredJwtException e) { // 토큰이 만료되었을 경우
			System.out.println(e);

		} catch (Exception e) { // 그 외의 예외 경우
			System.out.println(e);
		}
		return claimMap;
}
profile
벨로그보단 티스토리를 사용합니다! https://flight-developer-stroy.tistory.com/

2개의 댓글

comment-user-thumbnail
2023년 8월 5일

이런 유용한 정보를 나눠주셔서 감사합니다.

1개의 답글