티스토리 뷰

Backend(개발)

JWT 인증/인가 구현 방법

김남김 2025. 4. 14. 17:38

FriendShip 애플리케이션에서 인증/인가를 어떻게 구현 했는지 방법을 서술합니다. 인증/인가의 경우 구현 방법이 많습니다. 해당 방법이 완벽이 올바른 해답이라고 생각하지 않으며, 보다 나은 방법을 제시해주면 변경하도록 하겠습니다.

 


JWT란?

 

JWT(Jason Web Token)는 웹 표준으로서, 정보를 안전하게 전송하기 위해 JSON 객체를 사용하는 토큰을 정의하는 역할을 합니다. 주로 클라이언트와 서버 간의 정보를 전달하거나, 사용자 인증에 사용됩니다.


JWT의 구조

JWT는 세 부분으로 이루어져 있습니다

  1. Header(헤더): JWT의 헤더는 두 부분으로 이루어져 있습니다. 첫 번째 부분은 토큰의 종류를 지정하는데, 여기서는 "JWT"입니다. 두 번째 부분은 해시 알고리즘(예: HMAC SHA256 또는 RSA)을 지정합니다.
  2. { "typ": "JWT", "alg": "HS256" }
  3. Payload(내용): 페이로드는 클레임(claim)이라 불리는 정보의 조각들을 담고 있습니다. 클레임은 이름과 값의 쌍으로 이루어져 있으며, 세 가지 타입이 있습니다:
    • 등록된(Claims) 클레임: 표준 클레임으로, 토큰에 대한 정보를 제공합니다. 예시로는 발급자(iss), 만료 시간(exp), 발급 시간(iat) 등이 있습니다.
    • 공개(Public) 클레임: 충돌을 방지하기 위해 이름이 URI 형식으로 정의되어야 합니다. 어떠한 클레임도 이름이 충돌하지 않도록, URI 형식을 사용하는 것이 좋습니다.
    • 비공개(Private) 클레임: 토큰을 생성한 당사자와 수신자간에 사전에 정의한 클레임으로, 서로 협의된 정보를 담을 수 있습니다.
    {
      "memberId": "2",
      "name": "Aden",
      "role": 1
    }
    
  4. Signature(서명): 헤더와 페이로드의 인코딩 값을 비밀 키와 함께 서명하여 토큰을 생성합니다. 이 서명을 통해 토큰이 변경되지 않았음을 검증할 수 있습니다.
  5. HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret )

JWT는 토큰의 크기가 작고(JSON 형식으로 가볍기 때문에) 전송과 저장이 쉽다는 장점이 있습니다. 또한 토큰을 사용하여 클라이언트와 서버 간의 인증 및 정보 교환을 안전하게 할 수 있습니다.


JWT를 통한 인증/인가 동작 원리

로그인(토큰 발급)

  1. 사용자가 email 와 password 를 입력하여 로그인(토큰 발급) 을 시도합니다.
  2. 서버는 요청을 확인하고 secret key를 통해 회원을 DB에서 찾고, 토큰 발급을 위한 회원 정보를 토큰에 담아, AccessToken와 RefreshToken 을 발급합니다.
  • 생성된 RefreshToken은 토큰 재발급과 로그아웃에 사용되기에 DB에 memberId값과 함께 저장합니다.
  1. JWT 토큰(AccessToken와 RefreshToken)을 클라이언트에 전달 합니다.

회원 전용 API 접근(토큰 사용)

  1. 클라이언트에서 API 을 요청할 때,  클라이언트가 Authorization header에 Access token 만을 담아서 보냅니다.
  2. 서버는 JWT Signature를 체크하고 디코딩하여 Payload 에서 사용자 정보를 확인해 회원을 확인합니다.
  • 클라이언트의 로그인 정보를 서버 메모리에 저장하지 않기 때문에 토큰 기반 인증 메커니즘을 제공합니다. (로그인 정보를 DB에서 가져오는 것이 아니라 토큰에서 가져옵니다.)
  • 만약 AccessToken 이 만료된 상태라면 Reissue(재발급) 하도록 응답을 보냅니다.
  1. 해당 회원에 따른 API 요청에 맞게 응답을 클라이언트에게 전달합니다.

토큰 만료로 인한 리이슈 (토큰 재발급)

  1. 클라이언트는 RequestBody에 AccessToken과 RefreshToken을 담아 보냅니다.
  2. 서버는 AccessToken이 만료되었는지 확인합니다.
  • 만약 만료되지 않은 상태로 토큰 재발급을 요청할 경우 에러 응답을 보냅니다.
  1. 서버는 RefreshToken이 만료되었는지 확인 후, DB에 접근하여 만료 처리된 RefreshToken인지 검증합니다.
  • 이미 로그아웃 처리된 RefreshToken의 경우 해당 토큰은 유효하지 않기 때문에, 토큰을 재발급 하지 않고 에러 응답을 보냅니다.
  1. 서버는 RefreshToken에 들어간 memberId 값을 갖고 DB에 접근해 AccessToken을 발급을 위한 회원의 정보를 가져옵니다.
  2. 기존의 RefreshToken과 새로 생성된 AccessToken을 클라이언트에게 전달합니다.

로그아웃 (토큰 무효화)

  1. 클라이언트는 RequestBody에 AccessToken과 RefreshToken을 담아 보냅니다.
  2. 서버는 RefreshToken에서 memberId 를 가져오고 DB에 저장된 RefreshToken의 만료 여부 컬럼을 만료로 수정합니다.
  3. 클라이언트에게 정상적으로 토큰이 무효화되었음을 알립니다.

RefreshToken을 DB에 저장한 이유

  1. 공격자가 만약 AccessToken 을 탈취했을 경우 짧은 AccessToken의 만료 시간 때문에 악의를 가진 행위의 가능 시간을 줄일 수 있습니다.
  2. 만약 RefreshToken도 탈취하더라도, 만약 로그아웃이 처리된 RefreshToken을 가져왔더라면, AccessToken을 재발급을 통해 얻을 수 없습니다.

<쿠키*세션>  과 <JWT>를 통한 인증/인가 구현 방식의 비교

<jwt> 와 <쿠키*세션>을 통한 인증/인가 방식은 DB 접근에서 유의미한 차이가 일어납니다.

아래는 각 방식의 특징을 좀 더 상세하게 설명한 것입니다.

 

💡 쿠키*세션 방식

  1. 세션 기반 인증/인가:
    • 사용자의 인증 정보는 서버에 저장되고, 클라이언트는 세션 ID를 쿠키에 저장하여 유지합니다.
    • 세션 ID를 통해 서버는 사용자의 상태를 기억하고 인증/인가를 처리합니다.
  2. DB 접근 횟수:
    • 모든 요청에 대해 세션 정보를 확인하기 위해 DB에 접근합니다.
    • 사용자의 활동이 늘어날수록, 세션 정보를 위해 빈번한 DB 접근이 발생할 수 있습니다.
  3. 30분 기준 DB 접근 계산:
    • 30분 동안 1분에 5번의 서버 요청 시, 30 * 5 = 150번의 DB 접근이 발생합니다.

💡 JWT를 통한 방식:

  1. 토큰 기반 인증/인가:
    • 사용자의 인증 정보를 안전하게 토큰에 저장하고, 클라이언트는 토큰을 갖고 다니며 요청합니다.
    • 서버는 토큰을 검증하여 인증/인가를 처리합니다.
  2. DB 접근 횟수:
    • 토큰이 유효한 동안은 특별한 경우(예: 토큰 재발급)를 제외하고는 DB에 접근하지 않습니다.
  3. 30분 기준 DB 접근 계산:
    • 30분 동안 1번의 DB 접근이 발생하며, 토큰 재발급 시에만 추가적인 DB 접근이 필요합니다.

비교

  • 쿠키*세션 방식:
    • 간단하게 구현 가능하며, 서버 측에서 세션을 관리하므로 토큰 유출에 대한 우려가 적습니다.그러나 다수의 사용자 활동에서 DB 부하가 증가할 수 있습니다.
  • JWT 방식:
    • 클라이언트 측에서 토큰을 저장하므로 서버에 대한 부담이 감소합니다. 특히, 다중 서버 환경에서 확장이 용이합니다. 그러나 토큰이 유출될 경우 보안에 취약할 수 있습니다. 또한, 토큰 자체를 수정하거나 해독할 수 없도록 적절히 보호해야 합니다.
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
글 보관함