JWT

HYUK·2023년 2월 4일
0

1. JWT 란?

JWT는 Json Web Token의 약자로 일반적으로 클라이언트와 서버 사이에서 통신할 때 권한을 위해 사용하는 토큰이다. 웹 상에서 정보를 Json형태로 주고 받기 위해 표준규약에 따라 생성한 암호화된 토큰으로 복잡하고 읽을 수 없는 string 형태로 저장되어있다. 한줄로 정리하자면 '정보를 비밀리에 전달하거나 인증할 때 주로 사용하는 토큰으로, Json객체를 이용함'

2. JWT 인증순서

일반적으로 JWT를 사용하는 경우 아래와 같은 순서로 진행된다.

1). 사용자가 ID, PW를 입력하여 서버에 로그인 인증을 요청한다.

2). 서버에서 클라이언트로부터 인증 요청을 받으면, Header, PayLoad, Signature를 정의한다. Hedaer, PayLoad, Signature를 각각 Base64로 한 번 더 암호화하여 JWT를 생성하고 이를 쿠키 에 담아 클라이언트에게 발급한다.

4).클라이언트는 서버로부터 받은 JWT를 로컬 스토리지에 저장한다. (쿠키나 다른 곳에 저장할 수도 있음) API를 서버에 요청할때 Authorization header에 Access Token을 담아서 보낸다.

5). 서버가 할 일은 클라이언트가 Header에 담아서 보낸 JWT가 내 서버에서 발행한 토큰인지 일치 여부를 확인하여 일치한다면 인증을 통과시켜주고 아니라면 통과시키지 않으면 된다.

6). 인증이 통과되었으므로 페이로드에 들어있는 유저의 정보들을 select해서 클라이언트에 돌려준다.

7). 클라이언트가 서버에 요청을 했는데, 만일 액세스 토큰의 시간이 만료되면 클라이언트는 리프래시 토큰을 이용해서 서버로부터 새로운 엑세스 토큰을 발급 받는다.

3. JWT 구조

JWT는 아래와 같이 header, payload, signature 3가지로 구성되어 있다.

1). header

Header는 토큰의 타입과 해시 암호화 알고리즘으로 구성되어 있습니다. 첫째는 토큰의 유형 (JWT)을 나타내고, 두 번째는 HMAC, SHA256 또는 RSA와 같은 해시 알고리즘을 나타내는 부분이다.

2). payload

Payload는 토큰에 담을 클레임(claim) 정보를 포함하고 있다. 클레임은 사용자 id나 다른 데이터들, 이것들을 부르는데, 이는 name / value 의 한 쌍으로 이뤄져있다. 토큰에는 여러개의 클레임 들을 넣을 수 있다.이러한 이유로 JWT는 민감한 정보 교환을 위해 사용되기도 하고 현대 웹에서 가장 많이 쓰이는 예시는 인증&인가를 위해서 사용합니다. 쉽게 이야기하면, 신분증처럼 신원이 확인된 사람인지 아닌지 확인용으로 JWT를 쓰는 건데, 로그인 후할 수 있는 모든 활동에 JWT를 제시하고 인증된 사용자인지 허가를 받는 경우입니다.

❗️key-value 형식으로 이루어진 한 쌍의 정보를 Claim이라고 칭한다.

3). signature

가장 중요한 부분으로 헤더와 정보를 합친 후 발급해준 서버가 지정한 secret key로 암호화 시켜 토큰을 변조하기 어렵게 만들어준다. 토큰을 인코딩하거나 유효성 검증을 할 때 사용하는 고유한 암호화 코드이다.
간단한 예시를 들면, 토큰이 발급된 후 누군가가 Payload의 정보를 수정하면 Payload에는 다른 누군가가 조작된 정보가 들어가 있지만 Signatute에는 수정되기 전의 Payload 내용을 기반으로 이미 암호화 되어있는 결과가 저장되어 있기 때문에 조작되어있는 Payload와는 다른 결과값이 나오게 된다. 이러한 방식으로 비교하면 서버는 토큰이 조작되었는지 아닌지를 쉽게 알 수 있고, 다른 누군가는 조작된 토큰을 악용하기가 어려워진다

❗️위의 3가지 구조에 대해 정리하자면, Header와 Payload는 단순히 인코딩된 값이기 때문에 제 3자가 복호화 및 조작할 수 있지만, Signature는 서버 측에서 관리하는 비밀키가 유출되지 않는 이상 복호화할 수 없다. 따라서 Signature는 토큰의 위변조 여부를 확인하는데 사용된다.

JWT 사용방법

1). JWT 브라우저 storage 저장 방법

로그인 후 response의 body에 담긴 access token(JWT)을 브라우저의 저장소인 local storage, session storage, cookie에 저장할 수 있는데, local storage는 해당 도메인에 영구 저장하고 싶을 때, session storage는 해당 도메인의 한 세션에만 저장하고 싶을 때 창을 닫으면 저장한 data가 삭제된다. cookie는 해당 도메인에 날짜를 설정하고 그때까지만 저장하고 싶을 때 이렇게 각각의 용도에 따라 다르게 저장할 수 있다. 저장하는 방법을 설명하자면 로그인 요청 후 local storage에 토큰을 저장하는 방법을 보면,

// JWT 저장 예시

fetch('login api주소', {                               // 1
  method: 'POST',                                     // 2
  headers: {                                          // 3
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({                              // 4
    id: 'kim',
    password: '1234',
  }),
})
  .then((response) => {
    if (response.ok === true) {
      return response.json();                         // 5
    }
    throw new Error('통신실패!');                       // 6
  })
  .catch((error) => console.log(error))               // 7
  .then((data) => {
    if (data.message === 'SUCCESS') {                 // 8
      localStorage.setItem('token', data.token);
      alert('로그인 성공');
    } else if (data.message === 'INVALIDU_USER_ID') { // 9
      alert('아이디 혹은 비밀번호를 확인 해 주세요');
    }
  });

❗️ 위 코드 순서 정리 (주석으로된 번호와 매칭하며 확인)
1). 로그인하려는 API 주소로
2). POST 메서드 요청(request)을 보내는데
3). headers에 {'Content-Type': 'application/json'} 를 담고
4). body에 {'id': 'kim','password': '1234'}를 JSON으로 변환해서 요청한다.
5).첫 번째 응답에서 통신에 성공해 response.status가 200이면(혹은 다른 200번대의 코드면) JSON을 객체로 변환하거나,
6).실패했을 경우 error를 던져
7).catch로 error를 받아 console.log로 통신 실패 에러를 띄운다.
8).통신에 성공하고, 두 번째 응답에서 반환된 object의 key 중에 message가 SUCCESS라면 localStorage에 key는 'token', value는 data.token의 형태로 저장하고 alert창으로 메시지를 출력한다.
9). 만약 id가 틀리다면 메시지를 alert 창으로 띄우고 함수를 종료합니다.

2). 저장한 JWT 활용 방법

댓글을 달고 상품을 주문하고 상품을 장바구니에 담는 등 access token(JWT)을 받은 유저만 할 수 있는 기능들은 반드시 token으로 인증된 사용자라는 걸 서버에 알려야 한다. 그래서 이러한 작동을 하기 위해서는 localstorage에 저장된 토큰을 가져와야하는데, 이 저장된 데이터를 가져오는 메서드가 storage.getItem()이다.

// 브라우저 storage에서 JWT 가져오기 예시

const token = localStorage.getItem('token'); . 
//여기서 전달되는 인자는 토큰의 key 값을 string 타입으로 전달하면 된다.
//headers에 토큰 담아서 요청 코드 예시

const token = localStorage.getItem('token');

fetch('API주소', {
  method: 'GET',
  headers: {
    Authorization: token,
  },
})
  .then((response) => {
    if (response.ok === true) {
      return response.json();
    }
    throw new Error('통신실패');
  })
  .then((data) => console.log(data))
  .catch((error) => console.log(error));
profile
step by step

0개의 댓글