본문 바로가기
다양한 기술들/Web 관련

[JWT] 토큰을 통한 로그인 인증 관리

by 예스p 2023. 5. 2.

JWT 톺아보기

API를 사용하는 웹서비스를 개발한다면,

토큰을 사용해여 인증작업을 처리하는 것이 가장 좋은 방법이다.

토큰 기반 인증시스템에 주로 활용하는 JWT(Json Web Token)에 대해서 알아보자.


 

사용자 인증관리

기존에 사용하던 세션 방식과 새롭게 익힐 토큰 방식을 비교해본다.


세션(서버)기반 인증

  • 유저가 로그인을 했을 때 서버측에서 로그인중이라는 인증정보를 기억하는 방식
  • 토큰방식보다 보안에 강하다
  • 사용자들의 정보를 기억하기 위해서 세션을 유지해야함
    >> 단점1. 로그인 중인 유저의 수가 많아지면 성능에 무리가 간다.
     - 세션을 유지하기 위해 메모리를 활용시 램이 과부하되고, 이를 피하기 위해 데이터베이스에 저장하는 경우도 있지만 이 역시 DB성능에 무리가 간다
    >> 단점2. 확장성이 떨어진다.
     - 세션을 사용하면서 분산된 프로세스를 운영하면 동기화를 위한 복잡한 절차가 필요하다.
    >> 단점3. CORS 설정을 해야한다.
    - 웹 어플리케이션에서 세션을 관리할 때 자주 사용되는 쿠키는 단일 도메인 및 서브 도메인에서만 작동하도록 설계되어 있다. 따라서 쿠키를 여러 도메인에서 관리하는 것은 번거롭다.
    ** CORS(Cross-Origin Resource Sharing) : 브라우저는 보안상의 이유로 cross-origin HTTP 요청들을 제한하는데, 이 제한을 풀기 위해 HTTP-header에 설정하는 매커니즘.

<< 서버기반 인증시스템의 흐름>>
<<서버 방식 상세 과정>>

 

토큰기반 인증

  • 회원인증시 토큰을 발급해주고 유저의 요청시 헤더에 토큰을 포함시켜 유효성 검사를 한다.
    >> 서버측에서 유저의 정보를 유지하지 않는다.
  • 발급된 토큰은 토큰의 유효기간과 정보가 담겨있고 해싱알고리즘을 통한 인증되어있어 서버에서 검증을 통하여 처음 서버에서 발급해주었던 정보가 변질되지 않았음을 보장해줄 수 있다.
  • >> 장점1. 서버확장시 용이하다
    - 서버시스템이 분산되어있어도 유저는 하나의 토큰으로 서버에 요청을 하면 되고, 서버는 검증 후 별도의 DB 조회 없이 바로 유저임을 신뢰하고 처리할 수 있다.
    >> 장점2. 플랫폼 간 권한을 공유 할 수 있다.
    - OAuth 프로토콜과 함께 소셜 로그인에 활용된다.(구글 등의 플랫폼에서 로그인을 함으로써 토큰을 발행받고, 개발중인 백엔드 서버에서 이를 통해 회원정보를 가져옴으로써 계정 생성을 하는.)
    >> 장점3. 모바일 어플리케이션에 유리하다.
    - 세션기반 인증시에는 쿠키를 사용해야 하기 때문에 쿠키 매니저를 따로 관리해야하지만 토큰을 사용한다면 웹 요청 API 헤더에 넣어서 사용하면 된다.(CORS 해결)

<<토큰기반 인증시스템의 흐름>>

 


JWT개요

Json Web Token에 대해서 알아본다


JWT란?

  • Json 포맷을 이용하여 사용자에 대한 속성을 저장하는 Claim 기반의 Web Token
    * Claim : 페이로드로 포함된 정보조각
  • 토큰 자체를 정보로 사용하는 Self-Contained 방식
    (토큰안에 id, pw 등의 개인정보가 들어있다)
  • 토큰 보관방식
    1. 로컬스토리지 
       - 접근이 쉬워 보안에 취약하다
       - 토큰은 브라우저를 닫거나 사용자가 직접 로컬스토리지를 삭제할때 까지 유지
       - 애플리케이션에서 활용하려면 복잡성 증가
       - 용도 : 로그인시 저장, 로그아웃시 삭제
    2. static 변수
       - 어플리케이션의 메모리에 저장되므로 직접적 액세스 불가(보안강점)
       - 애플리케이션 종료되면 토큰도 사라짐
       - 토큰이 애플리케이션 전역에서 액세스 할 수 있어 코드의 단순화와 작업효율이 늘어남
       - 용도 : HTTP 통신시마다 헤더에 토큰을 담아 보내기(오버헤드를 방지에 유리)

 

 

JWT 동작과정

 

 

 

<<JWT 구조(좌 인코드 우 디코드)>>

JWT 구조

  • JWT는 Header, Payload, Signature의 3 부분으로 이루어지며, Json 형태인 각 부분은 Base64Url로 인코딩 되어 표현된다. 또한 각각의 부분을 이어 주기 위해 ' . ' 구분자를 사용하여 구분한다.
    * Base64Url : 암호화X, 같은 문자열에 대해 항상 같은 인코딩 문자열을 반환
  • 헤더(Header)
    - alg : 알고리즘 방식을 지정하며, 서명 및 토큰 검증에 사용
    - typ : 토큰의 타입을 지정(JWT)
  • 페이로드(Payload)
    - 토큰에서 사용할 정보의 조각들인 클레임(Claim)이 담겨있다.
    - 모든 이들에게 보이는 정보로 pw같은 정보를 담으면 안된다.
    - 클레임은 3가지로 나뉘며, JSON의 키/밸류 형태로 다수의 정보를 넣을 수 있다.
    1. 등록된 클레임(Registerd Claim)
       - 토큰 정보를 표현하기 위해 이미 정해진 종류의 데이터들(선택적으로 작성가능)
       * iss : 토큰 발급자(issuer)
       * sub : 토큰 주제(subject), 보통 id등을 담아 토큰발급의 대상이 되는 유저를 식별하는 역할을 한다.
       * aud : 토큰 대상자(audience)
       * exp : 만료 시간(expiration), NumericDate 형식으로 되어 있어야 함 ex) 1480849147370
       * nbf : not before, 이 날이 지나기 전의 토큰은 활성화 되지 않음
       * iat : issued at, 토큰이 발급된 시간 (NumericDate 형식)
       * jti : JWT ID, 중복 방지를 위한 식별자로 일회용 토큰 등에 사용
    2. 공개 클레임(Public Claim)
       - 사용자 정의 클레임
       - 공개용 정보를 표현한다.
       - 충돌 방지를 위해 URI 포멧을 사용한다.
    3. 비공개 클레임(Private Claim)
       - 사용자 정의 클레임
       - 서버와 클라이언트 사이에 임의로 지정한 정보를 저장
  • 서명(Signature)
    - 토큰을 인코딩하거나 유효성 검증을 할 때 사용하는 고유한 암호화 코드
    - 헤더와 페이로드의 값을 BASE64Url로 인코딩하고, 인코딩한 값 + 비밀키(Secret key) + 알고리즘(헤더에서 정의)으로 해싱을 하고, 이 값을 다시 BASE64Url로 인코딩하여 생성
    >> 비밀키(Secret key)를 포함해서 암호화가 되어있다!
{ // 헤더 예시
  "alg": "HS256",
  "typ": "JWT"
}
{ // 페이로드 예시
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022,
  "https://yes-p.tistory.com": true, // 공개클레임(충돌방지를 위해 URI 사용)
  "token_type": access // 비공개클레임
}
// 시그니처 예시
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

// 세가지로 완성된 토큰
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

 

 

생성된 토큰의 활용

  • 토큰의 생성
    - HTTP 요청헤더의 Authorization이라는 key의 value값으로 사용된다.
  • 토큰의 검증
    - 서버는 요청시 전달된 토큰을 헤더의 alg, kid 속성과 공개키를 이용해 검증한다.
    - 검증이 성공하면 페이로드의 값에 접근해서 사용한다.
  • Bearer 
    - JWT 토큰이 서버에 인증 요청을 보내는 것임을 나타내는 키워드
    - value값을 만들때 토큰값 앞에 붙인다. 
{   // HTTP 헤더에 토큰 담아서 보내기
    "Authorization": "Bearer eyJhbGkpX...(토큰값)...JV_adQssw5c",
 }

 

 

 

 

 

 

 

 

댓글