오늘은 프로젝트를 진행하면서 공부하고자 하는 내용 중 JWT에 대해 팀원들과 함께 알아보고자 하였다.
JWT란?
- Json Web Token의 약자
- 모바일 또는 웹의 사용자 인증을 위해 사용되는 암호화된 토큰이다.
- 로그인한 사용자에 대한 정보(데이터)들이 포함된다.
많은 토큰 인증 방식 중 하나인 JWT 인증 방식은 가장 많이 사용되는 토큰 인증 방식이라고도 한다.
JWT 토큰의 구조는 다음 예시와 같이 이루어져 있다.
# JWT 토큰 구조
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjU4ODQxNTE1LCJpYXQiOjE2NTg4Mzg1MTUsImp0aSI6ImI5ZDE1OTI2ODhlYTRjNjdiOWVmNTUzZGZmODk1NmZiIiwidXNlcl9pZCI6Mn0.73wxVS5MUUKY_F7hGtCDik1JdKwXb6FxRxcSg5zrugc
상당히 길고 복잡해보이는 해당 모습에서 마침표(.)로 구분지어진 부분이 있는데, 해당 부분은 'Header.Payload.Verify_Signature'의 형태로 구성되어 있다.
JWT 토큰의 구조에 해당되는 각 부분들에 대해 알아보자.
Header
- JWT를 검증하기 위해 필요한 정보를 가진 데이터
- Verify_Signature에서 사용한 암호화 알고리즘, 토큰 타입, key_id 등의 정보들을 지닌다.
# Header
{
"typ": "JWT", # 토큰 타입
"alg": "HS256", # 알고리즘
}
Payload
- 인증에 필요한 실질적인 데이터들을 저장한다.
- 사용자 정보를 인증할 때 사용하는 클레임(claim)이라는 데이터 필드를 가져와서 인증한다.
- Payload에서 가장 중요한 정보로는 토큰 발행 시간(iat)과 토큰 만료 시간(exp)가 있으며, 해당 토큰 만료 시간이 지나면 새로운 토큰을 재발급 받아야 한다.
# Payload
{
"token_type": "access", # 토큰의 종류
"exp": 1656293275, # 토큰 만료 시간
"iat": 1656293095, # 토큰 발행 시간
"jti": "2b45ec59cb1e4da591f9f647cbb9f6a3", # Json Token ID
"user_id": 1 # 실제 사용자 ID
}
Verify_Signature
- JWT 토큰 자체의 진위여부를 판단하기 위한 용도로 사용된다.
- Header와 Payload 두가지로는 토큰에 대한 진위여부를 판단할 수 없기 때문에 Verify_Signature를 사용한다.
# Verify_Signature
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
SECRET_KEY
)
이제 해당 JWT 인증 방식을 Django Rest Framework에서 사용해보도록 하자.
우선적으로 해당 기능을 사용할 수 있도록 터미널창에서 JWT 모듈을 설치하기 위한 명령어를 적어주도록 하자.
$ pip install djangorestframework-simplejwt
이제 JWT 방식을 이용하여 인증을 해볼 것이기 때문에 setting.py 내에 있는 'INSTALLED_APPS'와 'REST_FRAMEWORK' 안에 JWT 인증 방식을 추가해준다.
# settings.py
~
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'rest_framework_simplejwt', # JWT 인증 방식 추가
'user',
]
~
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [ # 기본적인 view 접근 권한 지정
'rest_framework.permissions.AllowAny'
],
'DEFAULT_AUTHENTICATION_CLASSES': [ # session 혹은 token을 인증 할 클래스 설정
'rest_framework.authentication.TokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
# JWT 인증 방식 추가
'rest_framework_simplejwt.authentication.JWTAuthentication',
],
'DEFAULT_PARSER_CLASSES': [ # request.data 속성에 액세스 할 때 사용되는 파서 지정
'rest_framework.parsers.JSONParser',
'rest_framework.parsers.FormParser',
'rest_framework.parsers.MultiPartParser'
]
}
해당 JWT 인증을 사용해보기 위해 'Django SimpleJWT'에서 기본으로 제공하고 있는 JWT 인증 방식을 사용해보도록 하자.
user/urls.py 내에 JWT 토큰 발급 URL을 설정해주자.
# user/urls.py
from django.urls import path
from rest_framework_simplejwt.views import (
TokenObtainPairView,
TokenRefreshView,
)
from .views import UserView, UserApiView
urlpatterns = [
# user/
path('', UserView.as_view()),
path('login/', UserApiView.as_view()),
path('logout/', UserApiView.as_view()),
path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
]
이제 Postman을 통해 로그인 API를 통해 JWT 인증 토큰이 제대로 발급되는지 확인해보자.

해당 유저 정보에 대한 refresh_token과 access_token이 제대로 발급되는 것을 볼 수 있었다.
발급받은 JWT 토큰에 대한 정보를 확인하고 싶다면 jwt.io 사이트를 이용하여 Header, Payload, Verify_Signature 3가지의 정보를 볼 수 있다.
JWT.IO
JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.
jwt.io

사이트에서 조회하는 방식 뿐만 아니라 Postman을 통해 해당 access_token 정보를 넣고 해당 유저에 대한 정보를 보기 위해 user/views.py에 JWT 인증 방식 클래스를 지정해준 뒤 확인을 해보았다.
# user/views.py
~
class UserView(APIView):
permission_classes = [permissions.AllowAny]
# JWT 인증방식 클래스 지정하기
authentication_classes = [JWTAuthentication]
def get(self, request):
user = request.user
return Response(UserSerializer(user).data)
이제 Postman에서 해당 토큰에 대한 사용자 조회를 하기 위해 Header에 Authorization을 추가해준 뒤 방금 발급받은 access_token을 넣어서 확인을 해보았다.

해당 access_token을 이용하여 확인을 하고 싶을 때는 Bearer를 앞에 붙여서 해당 토큰 인증 방식이 access_token임을 명시해주어야한다.
여기서 테스트를 할 때 발급받은 access_token의 유효 기간이 만료되어 사용하지 못하는 경우가 생기기도 하였다.
해당 토큰을 다시 발급 받으려면 로그인을 다시 하고, 또 기간이 만료되어 access_token 정보가 사라지면 다시 로그인을 하면서 발급을 받아야하는 상황이 생길 수 있다.
이때 로그인 시 같이 발급받았던 refresh_token을 사용하여 다시 access_token을 발급받을 수 있는데, refresh_token을 이용한 access_token 재발급도 한 번 진행해보도록 하자.
이미 user/urls.py에서 refresh URL을 설정해줬기 때문에 Postman에서 재발급 요청을 해주었다.

이제 재발급 받은 access_token을 가지고 사용자 정보를 조회해보도록 하자.

사진과 같이 재발급 받은 access_token을 이용하여 해당 사용자에 대한 정보를 조회할 수 있었다.
그러면 이전에 사용했던 access_token으로는 더이상 조회할 수 없을까?
다음 사진을 보면 알 수 있다.

이미 시간이 지나서 만료가 되었거나, 재발급으로 인해 이전 토큰에 대한 정보가 날라갔기 때문에 이전 토큰은 더이상 사용할 수 없게 된 것을 볼 수 있다.
마지막으로 따로 설정을 통해 직접 조정한 것이 아니라면 기본적으로 access_token 유효 시간은 5분, refresh_token 유효 시간은 1일로 지정되어 있을 것이다.
여기서 access_token의 유효 시간이 5분인 것을 보면 상당히 짧은 것을 볼 수 있는데, 코드를 구현하면서 따로 설정을 통해 해당 토큰들에 대한 유효 시간을 조정할 수 있다.
settings.py에 다음과 같은 코드를 추가해주면 된다.
# settings.py
from datetime import timedelta
SIMPLE_JWT = {
# Access 토큰 유효 시간 설정하기
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=50),
# Refresh 토큰 유효 시간 설정하기
'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
}
timedelta()를 이용하여 access_token과 refresh_token의 유효 시간을 각각 50분, 1일로 설정해줄 수 있었다.
이외에도 'SIMPLE_JWT' 내에 다른 설정들을 통해 직접 JWT 옵션들을 설정해줄 수도 있다.
# settings.py
from datetime import timedelta
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5),
'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
'ROTATE_REFRESH_TOKENS': False,
'BLACKLIST_AFTER_ROTATION': False,
'UPDATE_LAST_LOGIN': False,
'ALGORITHM': 'HS256',
'SIGNING_KEY': SECRET_KEY,
'VERIFYING_KEY': None,
'AUDIENCE': None,
'ISSUER': None,
'JWK_URL': None,
'LEEWAY': 0,
'AUTH_HEADER_TYPES': ('Bearer',),
'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION',
'USER_ID_FIELD': 'id',
'USER_ID_CLAIM': 'user_id',
'USER_AUTHENTICATION_RULE': 'rest_framework_simplejwt.authentication.default_user_authentication_rule',
'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
'TOKEN_TYPE_CLAIM': 'token_type',
'TOKEN_USER_CLASS': 'rest_framework_simplejwt.models.TokenUser',
'JTI_CLAIM': 'jti',
'SLIDING_TOKEN_REFRESH_EXP_CLAIM': 'refresh_exp',
'SLIDING_TOKEN_LIFETIME': timedelta(minutes=5),
'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=1),
}
-
오늘은 프로젝트를 진행하면서 JWT 토큰에 대한 것을 배워보았다.
팀원들 모두와 함께 스터디 하는 방식으로 배워보는 시간을 가졌는데, 확실히 혼자서 공부하는 것보다 함께 공부하는 것이 더 재미있고 이해가 잘 됐던 것 같다.
이제 내일부터는 Backend API를 다 구현한 상태에서 남은 프로젝트를 진행하며 무엇을 할지 서로 얘기를 해보는 시간을 가질 것 같다.
:P
'TIL 및 WIL > TIL (Today I Learned)' 카테고리의 다른 글
| [TIL] 2022.07.28 (TestCode 작성, My Little Trip(10)) (0) | 2022.07.28 |
|---|---|
| [TIL] 2022.07.27 (TestCode 작성, My Little Trip(9)) (0) | 2022.07.27 |
| [TIL] 2022.07.21 (MLT API 구현, My Little Trip(8)) (0) | 2022.07.21 |
| [TIL] 2022.07.19 (MLT API 구현, My Little Trip(7)) (0) | 2022.07.19 |
| [TIL] 2022.07.18 (MLT API 구현, My Little Trip(6)) (0) | 2022.07.18 |