JWT

개발자 강세영·2022년 5월 16일
0

TIL

목록 보기
31/70
post-custom-banner

JWT

JWT란

Json Web Token의 약자로 표준 인터넷 인증 방식 중 하나이다. 유저를 인증하고 식별하기 위해 사용한다.
처음 로그인할 때 서버가 JWT를 생성하여 클라이언트에게 주고난 뒤 유저가 인가가 필요한 어떤 활동을 할 때마다 JWT를 http header에 첨부하면 서버가 JWT를 검증하여 인가를 해준다.

JWT 인증 과정

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

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

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

  4. 서버가 할 일은 클라이언트가 Header에 담아서 보낸 JWT가 내 서버에서 발행한 토큰인지 일치 여부를 확인하여 일치한다면 인증을 통과시켜주고 아니라면 통과시키지 않으면 된다.
    인증이 통과되었으므로 페이로드에 들어있는 유저의 정보들을 select해서 클라이언트에 돌려준다.

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

JWT의 구조

  • header
  • payload
  • signature
    각 요소는 점으로 구분된다. 위 그림에서도 작은 점이 있는데 그게 구분점이다.

토큰의 타입(JWT)와 적용된 알고리즘(HS256)이 저장된다.

payload

사용자 또는 토큰에 대한 속성 정보가 저장된다. 속성 정보란 토큰의 생성시간, 만료시간, 토큰 대상자같은 것들 이다. 페이로드는 인코딩이 될뿐 암호화되는것이 아니기 때문에 민감한 정보를 담지 않는것이 좋으며 식별을 하기 위한 정보만 담는것이 좋다.

signature

헤더의 인코딩 값과 페이로드의 인코딩 값을 합친 후 비밀키로 해쉬를 하여 생성된것이다.
알고리즘은 header에서 정의한 것을 사용한다.
header와 payload는 단순히 인코딩만 된것이라 얼마든지 그 값을 알아낼 수 있지만 시그니처는 서버의 비밀키를 알아야 복호화가 가능하므로 시그니처는 토큰의 위변조 여부를 확인하는데 쓰인다.

파이썬에서 JWT 활용

pyjwt 패키지 설치
pip install pyjwt

pyjwt 말고 더 많은 기능을 제공하는 python-jose라는 패키지도 있다.

# 토큰 발행
import jwt # import pyjwt가 아님
from django.conf import settings # my_settings 임포트하는것보다 좋은 방법임.
# 그전에 my_settings에 SECRET_KEY, ALGORITHM(HS256등) 환경 변수 추가
...
access_token = jwt.encode({"id" : user.id}, settings.SECRET_KEY, algorithm = settings.ALGORITHM)
# 토큰 디코딩
payload = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])

JWT 발급시 만료시간 넣는방법

from datetime               import datetime, timedelta

만료시간을 넣기 위해 파이썬의 시간 관련 모듈을 임포트 해준다.

  • datetime.now() 시스템의 현재 시간을 불러오는 메서드
  • datetime.utcnow() UTC 기준 현재 시간을 불러오는 메서드
  • timedelta() 특정시간을 더하고 뺄때 사용하는 메서드
payload      = {'username': user.id, 'exp': datetime.utcnow() + timedelta(seconds=10)}
access_token = jwt.encode(payload, SECRET_KEY, algorithm=ALGORITHM)
return JsonResponse({'message': 'SUCCESS', 'access_token': access_token.decode('utf-8')}, status=200)
  • 만료시간은 'exp'로 쓰는게 일반적이다. 위 코드의 경우 만료시간은 10초다.

  • 처음엔 datetime.now()로 해봤는데 만료시간 확인이 안되서 utcnow()로 바꾸니 됐다, 이유는 아직 모르겠다

  • timedelta는 인자로 seconds, hours, days, weeks 를 받고, =를 넣어서 시간을 표현한다.

  • 현재시간 기준 3일 후를 유효기간으로 사용하고 싶다면 위의 예시처럼 timedelta(days=3)을 더해주면 되고, 3주면 timedelta(weeks=3)을 넣어주면 된다.

만료된 토큰에 대한 예외처리

except jwt.ExpiredSignatureError:
    return JsonResponse({"message": "EXPIRED_TOKEN"}, status = 400)

참고자료
https://inpa.tistory.com/entry/WEB-%F0%9F%93%9A-JWTjson-web-token-%EB%9E%80-%F0%9F%92%AF-%EC%A0%95%EB%A6%AC#Signature

post-custom-banner

0개의 댓글