본문 바로가기

Spring/Security

JWT 로그인 구현

로그인 정보를 Server 에 저장하지 않고, Client 에 로그인정보를 JWT 로 암호화하여 저장 → JWT 통해 인증/인가

 

  • 모든 서버에서 동일한 Secret Key 소유
  • Secret Key 통한 암호화 / 위조 검증 (복호화 시)

 

JWT 장/단점

  1. 장점
    • 클라이언트에 로그인 정보를 저장하기 때문에 동시 접속자가 많을 때 서버 측 부하 낮춤
    • Client, Sever 가 다른 도메인을 사용할 때(CORS 문제)
      • 예) 카카오 OAuth2 로그인 시 JWT Token 사용
  2. 단점
    • 구현의 복잡도 증가
    • JWT 에 담는 내용이 커질 수록 네트워크 비용 증가 (클라이언트 → 서버)
    • 기생성된 JWT 를 일부만 만료시킬 방법이 없음
    • Secret key 유출 시 JWT 조작 가능

JWT 사용 흐름 Overview

  1. Client 가 username, password 로 로그인 성공 시
    1. "로그인 정보" → JWT 로 암호화 (Secret Key 사용)

 

JWT Sample

    1. JWT 를 Client 응답에 전달
    2. Client 에서 JWT 저장 (쿠키, Local storage 등)
    1. Client 에서 JWT 통해 인증방법
      1. JWT 를 API 요청 시마다 Header 에 포함
        Content-Type: application/json
        **Authorization: Bearer** **<JWT>
        ...**
        
      2. 예) HTTP Headers
      3. Server
        1. Client 가 전달한 JWT 위조 여부 검증 (Secret Key 사용)
        2. JWT 유효기간이 지나지 않았는지 검증
        3. 검증 성공시,
          1. JWT → "로그인 정보" (UserDetailsImpl) 만들어 사용
          2. ex) GET /api/products : JWT 보낸 사용자의 관심상품 목록 조회
  • JWT 구조
    • JWT 는 누구나 평문으로 복호화 가능
    • 하지만 Secret Key 가 없으면 JWT 수정 불가능
    • → 결국 JWT 는 Read only 데이터

  1. Header: 사용자 정보
{ "alg": "HS256", "typ": "JWT" }
  1. Payload: 실제 data
{ "sub": "1234567890", "username": "제이홉", "admin": true }
  1. Signature: 인증정보를 위한 서명
HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

 

1. 클라이언트가 서버에 로그인 요청 -> username / password 인증

2. DB에 사용자가 존재하는지 확인하여 가입된 사용자라면 토큰을 생성하여 응답

3. 클라이언트는 발급받은 토큰을 헤더에 담아 서버가 허용한 API 사용

 

토큰값을 가지고 Decode해서 인증을 처리합니다.

따라서 토큰 정보만 가지고 있으면 여러개의 서버가 동시에 인증처리를 할 수 있습니다. 그래서 분산 시스템에 적합하다고 합니다.

 

  1. JWTAuthFilter : API 요청 Header 에 전달되는 JWT 유효성 인증
    1. 모든 API 에 대해 JWTAuthFilter 가 JWT 확인
    2. 로그인 전 허용이 필요한 API 는 예외처리 필요 ⇒ FilterSkipMatcher
      1. ex) 로그인 폼 페이지, 로그인 처리, css 파일 등
  2. FormLoginFilter : 회원 폼 로그인 요청 시 username / password 인증
  • 인증 성공 시 응답 (Response) 에 JWT 포함예) 응답 Header 에 아래 형태로 JWT 전달
  • : JWT 전달방법은 개발자가 정함
Authorization: BEARER <JWT>

ex)
Authorization: BEARER eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJzcGFydGEiLCJVU0VSTkFNRSI6IuultO2DhOydtCIsIlVTRVJfUk9MRSI6IlJPTEVfVVNFUiIsIkVYUCI6MTYxODU1Mzg5OH0.9WTrWxCWx3YvaKZG14khp21fjkU1VjZV4e9VEf05Hok

 

 

 

Refresh Token?

 

Access Token(JWT)를 통한 인증 방식의 문제는 만일 제 3자에게 탈취당할 경우 보안에 취약하다는 점입니다.

유효기간이 짧은 Token의 경우 그만큼 사용자는 로그인을 자주 해서 새롭게 Token을 발급받아야 하므로 불편합니다. 그러나 유효기간을 늘리자면, 토큰을 탈취당했을 때 보안에 더 취약해지게 됩니다. 

이때 “그러면 유효기간을 짧게 하면서  좋은 방법이 있지는 않을까?”라는 질문의 답이 바로 "Refresh Token"입니다. 

 

Refresh Token은 Access Token과 똑같은 형태의 JWT입니다. 처음에 로그인을 완료했을 때 Access Token과 동시에 발급되는 Refresh Token은 긴 유효기간을 가지면서, Access Token이 만료됐을 때 새로 발급해주는 열쇠가 됩니다(여기서 만료라는 개념은 그냥 유효기간을 지났다는 의미입니다.) 

 

사용 예를 간단히 들어보겠습니다. Refresh Token의 유효기간은 2주, Access Token의 유효기간은 1시간이라 하겠습니다. 사용자는 API 요청을 신나게 하다가 1시간이 지나게 되면, 가지고 있는 Access Token은 만료됩니다. 그러면 Refresh Token의 유효기간 전까지는 Access Token을 새롭게 발급받을 수 있습니다. 

 

 

 

** Access Token은 탈취당하면 정보가 유출되는건 동일합니다. 다만 짧은 유효기간 안에만 사용이 가능하기에 더 안전하다는 의미입니다. 

 

** Refresh Token의 유효기간이 만료됐다면, 사용자는 새로 로그인해야 합니다. Refresh Token도 탈취될 가능성이 있기 때문에 적절한 유효기간 설정이 필요해보입니다

 

Access Token + Refresh Token 인증 과정

이번에는 과정이 좀 복잡합니다. 차근차근 하나씩 알아봅시다. 빨간색은 1편의 JWT 인증방식에서 추가로 들어간 과정입니다.

 

1. 사용자가 ID , PW를 통해 로그인합니다.

 

2. 서버에서는 회원 DB에서 값을 비교합니다(보통 PW는 일반적으로 암호화해서 들어갑니다)

 

3~4. 로그인이 완료되면 Access Token, Refresh Token을 발급합니다. 이때 일반적으로 회원DB에 Refresh Token을 저장해둡니다.

 

5. 사용자는 Refresh Token은 안전한 저장소에 저장 후, Access Token을 헤더에 실어 요청을 보냅니다.

 

6~7. Access Token을 검증하여 이에 맞는 데이터를 보냅니다.

 

8. 시간이 지나 Access Token이 만료됐다고 보겠습니다.

 

9. 사용자는 이전과 동일하게 Access Token을 헤더에 실어 요청을 보냅니다.

 

10~11. 서버는 Access Token이 만료됨을 확인하고 권한없음을 신호로 보냅니다.

 

** Access Token 만료가 될 때마다 계속 과정 9~11을 거칠 필요는 없습니다.

 사용자(프론트엔드)에서 Access Token의 Payload를 통해 유효기간을 알 수 있습니다. 따라서 프론트엔드 단에서 API 요청 전에 토큰이 만료됐다면 바로 재발급 요청을 할 수도 있습니다.

 

 

12. 사용자는 Refresh Token과 Access Token을 함께 서버로 보냅니다.

 

13. 서버는 받은 Access Token이 조작되지 않았는지 확인한후, Refresh Token과 사용자의 DB에 저장되어 있던 Refresh Token을 비교합니다. Token이 동일하고 유효기간도 지나지 않았다면 새로운 Access Token을 발급해줍니다.

 

14. 서버는 새로운 Access Token을 헤더에 실어 다시 API 요청을 진행합니다. 

 

 

 

Refresh Token이 들어가면서 과정이 좀 복잡해졌습니다. 하지만 Access Token의 약점을 보완해주기 때문에 보안이 중요한 프로젝트에서는 사용하기를 권장합니다. 

 

 

 

(큰 장점)

 

기존의 Access Token만 있을 때보다 안전합니다.

 

 

(단점)

 

1. 구현이 복잡합니다. 검증 프로세스가 길기 때문에 자연스레 구현하기 힘들어졌습니다(프론트엔드, 서버 모두)

 

2. Access Token이 만료될 때마다 새롭게 발급하는 과정에서 생기는 HTTP 요청 횟수가 많습니다. 이는 서버의 자원 낭비로 귀결됩니다. 

 

 

레퍼런스

https://jwt.io/

 

JWT.IO

JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.

jwt.io