이전 글( Django - 회원가입/로그인 앱생성 개선(ver 1.2)암호화 및 토큰 발행)에서 JWT 토큰을 발행하는 사례를 소개한 적이 있었는데요, 이때는 토큰의 유효기간(만료기간)을 따로 설정하지 않았습니다. 테스트 환경이라 토큰이 계속 만료되면 번거롭기도 해서 뭐 이런저런 이유가 있었습니다.
그래서 이번에는 timedelta
를 이용해 현재로부터 일정 기간이 지난 시점까지 토큰의 유효기간을 설정하는 법을 알아보겠습니다.
일단 기본적인 JWT
토큰을 발행할 수 있다는 가정하에 아래와 같이 토큰 발행을 진행합니다.
토큰은 기본적으로 Header(헤더)
와 Payload(페이로드)
, Signature(시그니쳐)
로 나뉘는데, 유효기간은 claim set
이라고도 불리는 페이로드에 저장할 수 있습니다.
페이로드에는 토큰 만료시간이나 발급자, 제목과 같은 등록된 클레임이나 우리가 담고 싶은 내용을 담을 수 있는 공개/비공개 클레임을 담을 수 있습니다. 비공개 클레임은 클라이언트/서버 간에 서로 약속한 내용, 예를 들면 로그인 아이디 등을 담는 역할을 합니다.
등록된 클레임은 벨로퍼트님이 쓰신걸로 갈음하겠습니다.
iss: 토큰 발급자 (issuer)
sub: 토큰 제목 (subject)
aud: 토큰 대상자 (audience)
exp: 토큰의 만료시간 (expiraton), 시간은 NumericDate 형식으로 되어있어야 하며 (예: 1480849147370) 언제나 현재 시간보다 이후로 설정되어있어야합니다.
nbf: Not Before 를 의미하며, 토큰의 활성 날짜와 비슷한 개념입니다. 여기에도 NumericDate 형식으로 날짜를 지정하며, 이 날짜가 지나기 전까지는 토큰이 처리되지 않습니다.
iat: 토큰이 발급된 시간 (issued at), 이 값을 사용하여 토큰의 age 가 얼마나 되었는지 판단 할 수 있습니다.
jti: JWT의 고유 식별자로서, 주로 중복적인 처리를 방지하기 위하여 사용됩니다. 일회용 토큰에 사용하면 유용합니다.
아래의 코드를 보면, exp
에 만료기간을 설정하였습니다.
from datetime import datetime, timedelta
token = jwt.encode(
{'login_id':data['login_id'], 'exp':datetime.utcnow() + timedelta(days=3)},
SECRET_KEY, algorithm = 'HS256')
exp
부분을 보면 datetime.utcnow()
라는 현재 시간을 불러오는 메서드와 timedelta
라는 특정 시간을 더하고 뺄때 사용하는 메서드가 들어있습니다. 둘 다 datetime
에서 불러온 모듈을 사용합니다.
참고로 datetime.utcnow()
는 UTC 기준으로 시간을 불러오기 때문에, 실제 사용하는 시간대에 맞춰서 변경해 사용하셔야 합니다.
timedelta
는 인자로 seconds
, hours
, days
, weeks
를 받고, '='를 넣어서 시간을 표현합니다. 현재시간 기준 3일 후를 유효기간으로 사용하고 싶다면 위의 예시처럼 timedelta(days=3)
을 더해주면 되고, 3주면 timedelta(weeks=3)
을 넣어주면 됩니다.
months
나 years
는 좀 다르게 아래처럼 사용한다고 하네요.
from dateutil.relativedelta import relativedelta
exp = datetime.utcnow() - relativedelta(months=3)
이제 유효기간을 설정했으니, 사용자의 토큰을 확인할 때 이 유효기간을 통해 토큰이 유효한지 확인해야합니다. 일단 토큰이 유효한지 자체는 토큰을 decode
하는 과정에서 자동으로 진행해주기 때문에 따로 설정을 할 필요는 없습니다.
만료된 토큰을 넣어서 디코드를 진행하면 아래와 같은 에러가 나타납니다.
jwt.exceptions.ExpiredSignatureError: Signature has expired
말 그대로 토큰의 시그니쳐가 만료됐다는 얘긴데요, 이 에러를 Exception
로직에 넣어 처리해줘야합니다. 다행히 JWT
내에 에러를 잡아주는 녀석이 있어서 아래와 같이 에러를 캐치해 리턴을 넣어주면 됩니다.
except jwt.ExpiredSignatureError:
return JsonResponse({"message": "EXPIRED_TOKEN"}, status = 400)
이제 어느정도 이해가 가시는지 모르겠네요. 다음엔 리프레스 토큰에 대해서 알아봐야겠습니다.
장고는 가볍게 알아도, jwt에 대해선 많이 몰랐었는데, 덕분에 어떻게 장고에서 jwt으로 인증을 처리할 지 배웠습니다. 많이 배우고 갑니다 감사합니다 :-)