TokenObtainPairView는 JWT 토큰 로그인 방식에서 사용자 인증을 위해 Json Web Token(JWT) 토큰을 얻는데 사용되는 클래스 기반 뷰(CBV)입니다.
DRF의 simplejwt 패키지를 설치하면 사용할 수 있습니다.
[ Terminal ]
pip install djangorestframework-simplejwt
[ settings.py ]
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_simplejwt.authentication.JWTAuthentication',
)
}
[ urls.py ]
from rest_framework_simplejwt.views import TokenObtainPairView
urlpatterns = [
path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
]
엔드포인트 'api/token'로 요청을 보내면 DRF의 위 패키지에서 제공하는 기본 클래스 뷰인 TokenObtainPairView가 사용자에게 JWT 토큰을 발급합니다. (= 로그인)
클래스 기반 뷰(CBV)의 가장 큰 장점은 Reusable하다는 점입니다. 부모 클래스에 정의된 뷰 로직을 자식 클래스에서는 상속을 통해 필요한 메서드를 오버라이드하여 사용할 수 있습니다.
rest_framework_simplejwt.views의 TokenObtainPairView를 상속받아 새롭게 정의한 메서드를 통해 시리얼라이저를 커스터마이징 해보겠습니다.
[ urls.py ]
from django.urls import path
from users import views
urlpatterns = [
path('login/', views.LoginView.as_view(), name='login_view'),
]
[ TokenObtainView ]
class TokenObtainPairView(TokenViewBase):
"""
Takes a set of user credentials and returns an access and refresh JSON web
token pair to prove the authentication of those credentials.
"""
_serializer_class = api_settings.TOKEN_OBTAIN_SERIALIZER
token_obtain_pair = TokenObtainPairView.as_view()
[ views.py ]
from rest_framework_simplejwt.views import TokenObtainPairView
class LoginView(TokenObtainPairView):
serializer_class = LoginSerializer
[ serializers.py ]
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
class LoginSerializer(TokenObtainPairSerializer):
@classmethod
def get_token(cls, user):
token = super().get_token(user)
token['email'] = user.email
token['username'] = user.username
token['profile_img'] = user.profile_img.url
return token
토큰에 원하는 사용자 정보를 담을 수 있습니다.
_serializer_class = api_settings.TOKEN_OBTAIN_SERIALIZER
설정에 대해 settings.py에 재정의한 serializer를 TOKEN_OBTAIN_SERIALIZER
로 설정합니다. 이 설정에서는 토큰의 유효기간 등 여러 설정이 가능합니다.[ settings.py ]
SIMPLE_JWT = {
"ACCESS_TOKEN_LIFETIME": timedelta(minutes=720),
"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": "",
"AUDIENCE": None,
"ISSUER": None,
"JSON_ENCODER": 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),
#### customize한 serializer로 변경함.
"TOKEN_OBTAIN_SERIALIZER": "users.serializers.LoginSerializer",
#### "TOKEN_OBTAIN_SERIALIZER": "rest_framework_simplejwt.serializers.TokenObtainPairSerializer",
"TOKEN_REFRESH_SERIALIZER": "rest_framework_simplejwt.serializers.TokenRefreshSerializer",
"TOKEN_VERIFY_SERIALIZER": "rest_framework_simplejwt.serializers.TokenVerifySerializer",
"TOKEN_BLACKLIST_SERIALIZER": "rest_framework_simplejwt.serializers.TokenBlacklistSerializer",
"SLIDING_TOKEN_OBTAIN_SERIALIZER": "rest_framework_simplejwt.serializers.TokenObtainSlidingSerializer",
"SLIDING_TOKEN_REFRESH_SERIALIZER": "rest_framework_simplejwt.serializers.TokenRefreshSlidingSerializer",
}
[ login.html ]
<body>
<div>
<label>이메일:</label>
<input type="email" id="id_email"/>
</div>
<div>
<label>비밀번호:</label>
<input type="password" id="id_password"/>
</div>
<div>
<button type="button" onclick="handleLogin()">로그인</button>
</div>
</body>
이메일:비밀번호:로그인
[ login.js ]
async function handleLogin() {
const email = document.getElementById('id_email').value
const password = document.getElementById('id_password').value
const response = await fetch('http://127.0.0.1:8000/users/login/', {
headers : {
'Content-type': 'application/json',
},
method : 'POST',
body : JSON.stringify({
"email" : email,
"password" : password
})
})
if (response.status == 200) {
const response_json = await response.json()
localStorage.setItem("access", response_json.access)
localStorage.setItem("refresh", response_json.refresh)
const base64Url = response_json.access.split('.')[1];
const base64 = base64Url.replace(/-/g, '+').replace(/_/g,'/');
const jsonPayload = decodeURIComponent(atob(base64).split('').map(function(c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
}).join(''));
localStorage.setItem("payload",jsonPayload);
alert('로그인 성공')
window.location.href = "/wish/main.html"
}
JWT 토큰 로그인 방식에서의 로그아웃은 사용자의 정보가 저장된 토큰을 로컬스토리지에서 없애주면 됩니다.
<button type="button" onclick="handleLogout()">로그아웃</button>
로그아웃
function handleLogout() {
localStorage.removeItem('access')
localStorage.removeItem('refresh')
localStorage.removeItem('payload')
}
🥚🐣🐤🐥🐓🐔