JWT 기본 개념 정리

·2024년 3월 14일

Spring Boot

목록 보기
3/4
post-thumbnail

JWT 기본 개념

참고 자료 : 우아한 기술 블로그
https://techblog.woowahan.com/9135/

⭐ JWT란?

Json web Token 은 통신을 수행하는 두 대상이 서로 보낸 데이터에 대하여 신뢰할 수 있게 데이터가 인코딩된 Token 을 의미

  • Header Token 의 타입, 서명에 사용된 암호화 알고리즘
  • Payload : 데이터
  • Signature : Header 에 명시된 암호화 알고리즘과 secret key 를 이용하여 Header 와 Payload 를 서명(암호화) 한 값

📍 JWT 장점

Stateless Token 으로 서버가 인증 작업을 수행하지 않는다


📍 JWT 단점

Stateless Token 으로 서버가 인증 작업을 수행하지 않는다 서버가 인증 토큰에 대한 제어권을 잃어, 관리할 방법이 없음.
=> 로직이 없어 여러명의 사용자가 api 요청을 했을 때, 서버를 거치지 않아 실행 시간이 빨라지고 서버에 부하가 없다


⭐ JWT 를 이용한 인증

  • 사용자가 로그아웃을 하고 싶어도, expried time 이 되지 않으면 Token 이 만료되지 않아 실질적으로 로그아웃이 되지 않음
  • Token 이 탈취되었을 경우 임의로 서버에서 토큰을 만료시킬 수 없음
  • JWT Token 을 JS 를 이용하여 base64 decoding 해서 사용해야 하다 보니 Token 을 쿠키로 받을 경우 httponly 옵션을 추가할 수 없으며, 쿠키가 아닌 경우 localStarage 와 같은 브라우저 내장 storage 를 사용해야 하기 때문에 XSS 로 인한 Token 탈취 가능성이 높아짐

    ❓ XXS 란?
    크로스 사이트 스크립팅 또는 교차 사이트 스크립팅(Cross Site Scripting, XSS)은 공격자가 상대방의 브라우저에 스크립트가 실행되도록 해 사용자의 세션을 가로채거나, 웹사이트를 변조하거나, 악의적 콘텐츠를 삽입하거나, 피싱 공격을 진행하는 것을 말합니다.

⭐ 조취 방안

  • JWT BlackList 를 구현하여 같이 운영함으로써, 서버에서 Token 에 대한 제어를 수행하도록 구현한다.
  • JWT Payload 내 stateful 한 token 을 추가하여, 서버에서 stateful token 을 통해 제어를 수행할 수 있도록 구현한다.
  • Token 에 대한 유효성 검증 로직이 서버에 추가됨에 따라 기존 JWT 에 비하여 서버 부하가 조금 증가하겠으나, Stateful token 만을 이용한 방식보다는 여전히 부하가 적음.

⭐ JWT 동작 방식

📍 자가수용적(self-contained)인 방식

jwt 는 필요한 모든 정보를 자체적으로 지니고 있다는 장점이 있다. 이는 jwt 에서 발급된 토큰은 기본 정보, 전달할 정보, 검증에 대한 모든 signature 도 포함하고 있으며, 웹 서버 환경에서는 HTTP 헤더에 포함시키거나 URL 의 파라미터로도 전달이 가능하다.

📍 JWT 동작 프로세스

(1) 최초 호출
클라이언트에서 서버로 최초 호출이 발생하는 경우 Header 내에 토큰의 존재 여부를 체크한다.

  • 토큰이 존재하지 않을 경우 '로그인' 페이지로 리다이렉션 시켜 로그인 후 토큰을 발급 받는다.
  • 토큰이 존재하는 경우 토큰의 만료 시간을 확인한다.
  • 토큰이 만료되었을 경우 재발급을 위해 '로그인' 페이지로 리다이렉트 시켜 로그인 후 토큰을 재발급 받는다.
  • 토큰이 유효한 경우 토큰 내의 정보를 확인하여 가져온다.

(2) 이후 호출
클라이언트에서 서버로 이후 호출이 발경하는 경우, 토큰의 만료 시간을 확인한다.

  • 토큰이 만료된 경우 로그인 페이지로 리다이렉션 시켜 로그인 후 토큰을 발급받는다.
  • 토큰이 만료되지 않은 경우 정상적인 프로세스를 진행한다.

⭐ JWT 구조 이해하기

📍 (1) JWT 구조

aaaaaa.bbbbbb.cccccc
header.payload.signature

  • jwt 는 점으로 구분된 세 부분으로 구성되어 있다.
  • 이는 서버에서 각각 header, payload, signature 를 구성한 값을 인코딩하여서 사용된다

📍 (2) Header

  • 헤더의 구성은 토큰의 타입과 서명(Signature)에서 사용할 알고리즘으로 구성되어 있다.
  • 알고리즘 종류 : HMAC, SHA256, RSA, HS256, RS256
  • 구성한 헤더(Header)의 JSON은 ‘Base64Url’로 인코딩 되어서 JSON 웹 토큰의 첫 번째 요소로 사용됨

{
"typ": "JWT", // 토큰의 타입
"alg": "HS256" // 토큰 서명에서 사용할 알고리즘의 종류
}

/**
 * 헤더 값을 생성해주는 메서드
 *
 * @return HashMap<String, Object> 헤더 값
 */
private static Map<String, Object> createHeader() {
    Map<String, Object> header = new HashMap<>();
    header.put("typ", "JWT");
    header.put("alg", "HS256");
    header.put("regDate", System.currentTimeMillis());
    return header;
}

📍 (3) payload

  • 페이로드의 구성은 인증을 위해 사용할 실제 정보들(클레임)으로 구성되어 있다
  • 클레임의 종류는 등록 클레임(registered clains), 공개 클레임(public claims), 비공개 클레임(private claims)의 세 가지로 구성되어 있다.
  • 클레임은 페이로드 구성에 담은 key 와 value로 이루어진 한 쌍의 형태를 클레임이라고 한다.

{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}

/**
 * 사용자 정보를 기반으로 클래임을 생성해주는 메서드
 *
 * @param userDto 사용자 정보
 * @return Map<String, Object>
 */
private static Map<String, Object> createClaims(UserDto userDto) {
    // 공개 클레임에 사용자의 이름과 이메일을 설정하여 정보를 조회할 수 있다.
    Map<String, Object> claims = new HashMap<>();

    log.info("userId :" + userDto.getUserId());
    log.info("userNm :" + userDto.getUserNm());

    claims.put("userId", userDto.getUserId());
    claims.put("userNm", userDto.getUserNm());
    return claims;
}

📍 (4) 서명(signature)

  • 백엔드에서 발급되는 서명(Signature)은 인코딩된 헤더 (Header)와 인코딩된 페이로드(payload), 비밀 키(secret key)와 헤더에 지정된 서명에 사용할 알고리즘을 기반으로 발급된다
  • 서명(Signature) = 인코딩 헤더 + 인코딩 비밀 키 + 비밀 키 + 헤더의 알고리즘

HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
private static String jwtSecretKey = "jwtSecretKey";

/**
 * JWT 서명(Signature) 발급을 해주는 메서드
 *
 * @return Key
 */
private static Key createSignature() {
    byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(jwtSecretKey);
    return new SecretKeySpec(apiKeySecretBytes, SignatureAlgorithm.HS256.getJcaName());
}
profile
자바 백엔드 개발자 개인 위키

0개의 댓글