이 전에는 장고의 Pyjwt를 이용한 로그인 기능을 구현했었습니다!
하지만, 이 pyjwt는 더 이상의 업데이트가 이루어지지않고 pyjwt보다 django의 simple-jwt가 더 용이하다고하여 현재 진행중인 프로젝트는 이 simple-jwt를 이용하여 구현하였습니다!😊
전에 토큰인증방식에 대하여 먼저 공부한적도 있었고, pyjwt를 이용하여 구현도 하였었기에 쉽게 simple-jwt를 써서 기능을 구현할 수 있었던 것 같습니다.
먼저 POST 메소드로 로그인을 하였을 때, 토큰을 생성하여 이를 쿠키에 담아 전송하는 코드는 다음과 같습니다.
# 로그인 : access, refresh 토큰 생성
def post(self, request):
email = request.data['email']
password = request.data['password']
user = User.objects.filter(email=email).first()
if user is None: # 해당 email의 user가 존재하지 않는 경우
return Response(
{"message": "존재하지않는 email입니다."}, status=status.HTTP_400_BAD_REQUEST
)
if not check_password(password, user.password): # 비밀번호에서 틀린 경우
return Response(
{"message": "비밀번호가 틀렸습니다."}, status=status.HTTP_400_BAD_REQUEST
)
if user is not None: # 모두 성공 시
token = TokenObtainPairSerializer.get_token(user)
refresh_token = str(token)
access_token = str(token.access_token)
response = Response(
{
"user": UserSeriallizer(user).data,
"message": "login success",
"jwt_token": {
"access_token": access_token,
"refresh_token": refresh_token,
},
},
status=status.HTTP_200_OK
)
response.set_cookie("access_token", access_token, httponly=True)
response.set_cookie("refresh_token", refresh_token, httponly=True)
return response
else: # 그 외
return Response(
{"message": "로그인에 실패하였습니다"}, status=status.HTTP_400_BAD_REQUEST
)
먼저, 소개해드릴 내장 serializer가 2개가 있는데요.
그 중 첫번째는 TokenObtainPairSerializer입니다.
이를 까보면 다음과 같습니다.
class RefreshToken(BlacklistMixin, Token):
token_type = 'refresh'
lifetime = api_settings.REFRESH_TOKEN_LIFETIME
no_copy_claims = (
api_settings.TOKEN_TYPE_CLAIM,
'exp',
# Both of these claims are included even though they may be the same.
# It seems possible that a third party token might have a custom or
# namespaced JTI claim as well as a default "jti" claim. In that case,
# we wouldn't want to copy either one.
api_settings.JTI_CLAIM,
'jti',
)
@property
def access_token(self):
"""
Returns an access token created from this refresh token. Copies all
claims present in this refresh token to the new access token except
those claims listed in the `no_copy_claims` attribute.
"""
access = AccessToken()
# Use instantiation time of refresh token as relative timestamp for
# access token "exp" claim. This ensures that both a refresh and
# access token expire relative to the same time if they are created as
# a pair.
access.set_exp(from_time=self.current_time)
no_copy = self.no_copy_claims
for claim, value in self.payload.items():
if claim in no_copy:
continue
access[claim] = value
return access
class AccessToken(Token):
token_type = 'access'
lifetime = api_settings.ACCESS_TOKEN_LIFETIME
class TokenObtainSerializer(serializers.Serializer):
username_field = get_user_model().USERNAME_FIELD
default_error_messages = {
'no_active_account': _('No active account found with the given credentials')
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields[self.username_field] = serializers.CharField()
self.fields['password'] = PasswordField()
def validate(self, attrs):
authenticate_kwargs = {
self.username_field: attrs[self.username_field],
'password': attrs['password'],
}
try:
authenticate_kwargs['request'] = self.context['request']
except KeyError:
pass
self.user = authenticate(**authenticate_kwargs)
if not api_settings.USER_AUTHENTICATION_RULE(self.user):
raise exceptions.AuthenticationFailed(
self.error_messages['no_active_account'],
'no_active_account',
)
return {}
@classmethod
def get_token(cls, user):
raise NotImplementedError('Must implement `get_token` method for `TokenObtainSerializer` subclasses')
class TokenObtainPairSerializer(TokenObtainSerializer):
@classmethod
def get_token(cls, user):
return RefreshToken.for_user(user)
def validate(self, attrs):
data = super().validate(attrs)
refresh = self.get_token(self.user)
data['refresh'] = str(refresh)
data['access'] = str(refresh.access_token)
if api_settings.UPDATE_LAST_LOGIN:
update_last_login(None, self.user)
return data
저는 다음과 같이 이용하였습니다.
token = TokenObtainPairSerializer.get_token(user) # refresh토큰 생성
refresh_token = str(token)
access_token = str(token.access_token) # access토큰 생성
그리고 이 둘을 쿠키에 넣어서 응답과 함께 전달하였습니다!
이 응답받은 토큰을 이용하여 유저를 식별하는것, access토큰이 만료되었을 때 refresh토큰을 이용하여 access토큰을 재발급하는것은 내일 마저 업로드하도록 하겠습니다!☺️