티스토리 뷰
FriendShip 애플리케이션에서 인증/인가를 어떻게 구현 했는지 방법을 서술합니다. 인증/인가의 경우 구현 방법이 많습니다. 해당 방법이 완벽이 올바른 해답이라고 생각하지 않으며, 보다 나은 방법을 제시해주면 변경하도록 하겠습니다.
JWT란?
JWT(Jason Web Token)는 웹 표준으로서, 정보를 안전하게 전송하기 위해 JSON 객체를 사용하는 토큰을 정의하는 역할을 합니다. 주로 클라이언트와 서버 간의 정보를 전달하거나, 사용자 인증에 사용됩니다.
JWT의 구조
JWT는 세 부분으로 이루어져 있습니다
- Header(헤더): JWT의 헤더는 두 부분으로 이루어져 있습니다. 첫 번째 부분은 토큰의 종류를 지정하는데, 여기서는 "JWT"입니다. 두 번째 부분은 해시 알고리즘(예: HMAC SHA256 또는 RSA)을 지정합니다.
- { "typ": "JWT", "alg": "HS256" }
- Payload(내용): 페이로드는 클레임(claim)이라 불리는 정보의 조각들을 담고 있습니다. 클레임은 이름과 값의 쌍으로 이루어져 있으며, 세 가지 타입이 있습니다:
- 등록된(Claims) 클레임: 표준 클레임으로, 토큰에 대한 정보를 제공합니다. 예시로는 발급자(iss), 만료 시간(exp), 발급 시간(iat) 등이 있습니다.
- 공개(Public) 클레임: 충돌을 방지하기 위해 이름이 URI 형식으로 정의되어야 합니다. 어떠한 클레임도 이름이 충돌하지 않도록, URI 형식을 사용하는 것이 좋습니다.
- 비공개(Private) 클레임: 토큰을 생성한 당사자와 수신자간에 사전에 정의한 클레임으로, 서로 협의된 정보를 담을 수 있습니다.
{ "memberId": "2", "name": "Aden", "role": 1 }
- Signature(서명): 헤더와 페이로드의 인코딩 값을 비밀 키와 함께 서명하여 토큰을 생성합니다. 이 서명을 통해 토큰이 변경되지 않았음을 검증할 수 있습니다.
- HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret )
JWT는 토큰의 크기가 작고(JSON 형식으로 가볍기 때문에) 전송과 저장이 쉽다는 장점이 있습니다. 또한 토큰을 사용하여 클라이언트와 서버 간의 인증 및 정보 교환을 안전하게 할 수 있습니다.
JWT를 통한 인증/인가 동작 원리
로그인(토큰 발급)
- 사용자가 email 와 password 를 입력하여 로그인(토큰 발급) 을 시도합니다.
- 서버는 요청을 확인하고 secret key를 통해 회원을 DB에서 찾고, 토큰 발급을 위한 회원 정보를 토큰에 담아, AccessToken와 RefreshToken 을 발급합니다.
- 생성된 RefreshToken은 토큰 재발급과 로그아웃에 사용되기에 DB에 memberId값과 함께 저장합니다.
- JWT 토큰(AccessToken와 RefreshToken)을 클라이언트에 전달 합니다.
회원 전용 API 접근(토큰 사용)
- 클라이언트에서 API 을 요청할 때, 클라이언트가 Authorization header에 Access token 만을 담아서 보냅니다.
- 서버는 JWT Signature를 체크하고 디코딩하여 Payload 에서 사용자 정보를 확인해 회원을 확인합니다.
- 클라이언트의 로그인 정보를 서버 메모리에 저장하지 않기 때문에 토큰 기반 인증 메커니즘을 제공합니다. (로그인 정보를 DB에서 가져오는 것이 아니라 토큰에서 가져옵니다.)
- 만약 AccessToken 이 만료된 상태라면 Reissue(재발급) 하도록 응답을 보냅니다.
- 해당 회원에 따른 API 요청에 맞게 응답을 클라이언트에게 전달합니다.
토큰 만료로 인한 리이슈 (토큰 재발급)
- 클라이언트는 RequestBody에 AccessToken과 RefreshToken을 담아 보냅니다.
- 서버는 AccessToken이 만료되었는지 확인합니다.
- 만약 만료되지 않은 상태로 토큰 재발급을 요청할 경우 에러 응답을 보냅니다.
- 서버는 RefreshToken이 만료되었는지 확인 후, DB에 접근하여 만료 처리된 RefreshToken인지 검증합니다.
- 이미 로그아웃 처리된 RefreshToken의 경우 해당 토큰은 유효하지 않기 때문에, 토큰을 재발급 하지 않고 에러 응답을 보냅니다.
- 서버는 RefreshToken에 들어간 memberId 값을 갖고 DB에 접근해 AccessToken을 발급을 위한 회원의 정보를 가져옵니다.
- 기존의 RefreshToken과 새로 생성된 AccessToken을 클라이언트에게 전달합니다.
로그아웃 (토큰 무효화)
- 클라이언트는 RequestBody에 AccessToken과 RefreshToken을 담아 보냅니다.
- 서버는 RefreshToken에서 memberId 를 가져오고 DB에 저장된 RefreshToken의 만료 여부 컬럼을 만료로 수정합니다.
- 클라이언트에게 정상적으로 토큰이 무효화되었음을 알립니다.
RefreshToken을 DB에 저장한 이유
- 공격자가 만약 AccessToken 을 탈취했을 경우 짧은 AccessToken의 만료 시간 때문에 악의를 가진 행위의 가능 시간을 줄일 수 있습니다.
- 만약 RefreshToken도 탈취하더라도, 만약 로그아웃이 처리된 RefreshToken을 가져왔더라면, AccessToken을 재발급을 통해 얻을 수 없습니다.
<쿠키*세션> 과 <JWT>를 통한 인증/인가 구현 방식의 비교
<jwt> 와 <쿠키*세션>을 통한 인증/인가 방식은 DB 접근에서 유의미한 차이가 일어납니다.
아래는 각 방식의 특징을 좀 더 상세하게 설명한 것입니다.
💡 쿠키*세션 방식
- 세션 기반 인증/인가:
- 사용자의 인증 정보는 서버에 저장되고, 클라이언트는 세션 ID를 쿠키에 저장하여 유지합니다.
- 세션 ID를 통해 서버는 사용자의 상태를 기억하고 인증/인가를 처리합니다.
- DB 접근 횟수:
- 모든 요청에 대해 세션 정보를 확인하기 위해 DB에 접근합니다.
- 사용자의 활동이 늘어날수록, 세션 정보를 위해 빈번한 DB 접근이 발생할 수 있습니다.
- 30분 기준 DB 접근 계산:
- 30분 동안 1분에 5번의 서버 요청 시, 30 * 5 = 150번의 DB 접근이 발생합니다.
💡 JWT를 통한 방식:
- 토큰 기반 인증/인가:
- 사용자의 인증 정보를 안전하게 토큰에 저장하고, 클라이언트는 토큰을 갖고 다니며 요청합니다.
- 서버는 토큰을 검증하여 인증/인가를 처리합니다.
- DB 접근 횟수:
- 토큰이 유효한 동안은 특별한 경우(예: 토큰 재발급)를 제외하고는 DB에 접근하지 않습니다.
- 30분 기준 DB 접근 계산:
- 30분 동안 1번의 DB 접근이 발생하며, 토큰 재발급 시에만 추가적인 DB 접근이 필요합니다.
비교
- 쿠키*세션 방식:
- 간단하게 구현 가능하며, 서버 측에서 세션을 관리하므로 토큰 유출에 대한 우려가 적습니다.그러나 다수의 사용자 활동에서 DB 부하가 증가할 수 있습니다.
- JWT 방식:
- 클라이언트 측에서 토큰을 저장하므로 서버에 대한 부담이 감소합니다. 특히, 다중 서버 환경에서 확장이 용이합니다. 그러나 토큰이 유출될 경우 보안에 취약할 수 있습니다. 또한, 토큰 자체를 수정하거나 해독할 수 없도록 적절히 보호해야 합니다.
'Backend(개발)' 카테고리의 다른 글
테스트 시 DB 초기화 작업 자동화 (0) | 2025.04.14 |
---|---|
SpringMVC 확장을 통한 인증/ 인가 구현 (0) | 2025.04.14 |
보안상 분리된 환경 변수 파일의 관리법 (0) | 2025.04.14 |
Applicaiotn.yml, properties을 지키기 (0) | 2025.04.14 |
배포 오류 : Asciidoctor 빌드 오류(asGemPath()) (0) | 2025.04.14 |