이번 포스팅에서는 JWT를 생성하고 로그인,로그아웃 기능을 구현하는 방법을 정리해 보려고 한다.
로그인 로직
- 클라이언트는 LoginApi를 호출하면서 {"username": "이름", "password": "비번"} 정보를 전달해준다.
- 서버는 username과 password를 가지고, 해당하는 유저를 찾은 다음 jwt_login을 수행한다.
- jwt_login에서는 access_token과 refresh_token을 생성한다.
- 생성된 access_token은 {"access_token": access_token}형태의 json으로 클라이언트에 전달되고,
생성된 refresh_token은 httpOnly=True 속성을 가진채로 cookie에 삽입된다.
로그아웃 로직
- 클라이언트에서 LogoutApi를 호출한다.
- 호출과 동시에 클라이언트는 가지고 있던 access_token을 삭제한다.
- 서버에서는 cookie에 존재하는 refreshtoken을 삭제한다.
refreshtoken을 삭제함으로써 완전히 로그아웃을 할 수 있다.
access_token 추가 발급
- 클라이언트에서 RefreshJWTtoken을 호출한다.
- 서버는 cookie에 있는 refreshjwttoken을 읽어서 decode한다.
- decode한 정보로 유저를 추출하고, 로그인 할 때와 마찬가지로 access_token을 새로 만들어서 json 형태로 전달한다.
구현 코드
LoginAPi, LogoutApi, RefreshJWTtoken
## auth/apis.py
import jwt
from rest_framework import status
from rest_framework.views import APIView
from rest_framework.response import Response
from django.contrib.auth import get_user_model
from django.conf import settings
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_protect, ensure_csrf_cookie
from auth.authenticate import generate_access_token, jwt_login
from api.mixins import PublicApiMixin
User = get_user_model()
@method_decorator(ensure_csrf_cookie, name="dispatch")
class LoginApi(PublicApiMixin, APIView):
def post(self, request, *args, **kwargs):
"""
username 과 password를 가지고 login 시도
key값 : username, password
"""
user = User
username = request.data.get('username')
password = request.data.get('password')
if (username is None) or (password is None):
return Response({
"message": "username/password required"
}, status=status.HTTP_400_BAD_REQUEST)
user = User.objects.filter(username=username).first()
if user is None:
return Response({
"message": "유저를 찾을 수 없습니다"
}, status=status.HTTP_404_NOT_FOUND)
if not user.check_password(password):
return Response({
"message": "wrong password"
}, status=status.HTTP_400_BAD_REQUEST)
response = Response(status=status.HTTP_200_OK)
return jwt_login(response, user)
@method_decorator(csrf_protect, name='dispatch')
class RefreshJWTtoken(PublicApiMixin, APIView):
def post(self, request, *args, **kwargs):
refresh_token = request.COOKIES.get('refreshtoken')
if refresh_token is None:
return Response({
"message": "Authentication credentials were not provided."
}, status=status.HTTP_403_FORBIDDEN)
try:
payload = jwt.decode(
refresh_token, settings.REFRESH_TOKEN_SECRET, algorithms=['HS256']
)
except:
return Response({
"message": "expired refresh token, please login again."
}, status=status.HTTP_403_FORBIDDEN)
user = User.objects.filter(id=payload['user_id']).first()
if user is None:
return Response({
"message": "user not found"
}, status=status.HTTP_400_BAD_REQUEST)
if not user.is_active:
return Response({
"message": "user is inactive"
}, status=status.HTTP_400_BAD_REQUEST)
access_token = generate_access_token(user)
return Response(
{
'access_token': access_token,
}
)
@method_decorator(csrf_protect, name='dispatch')
class LogoutApi(PublicApiMixin, APIView):
def post(self, request):
"""
클라이언트 refreshtoken 쿠키를 삭제함으로 로그아웃처리
"""
response = Response({
"message": "Logout success"
}, status=status.HTTP_202_ACCEPTED)
response.delete_cookie('refreshtoken')
return response
generate_access_token, generate_refresh_token, jwt_login
## auth/authenticate.py
import datetime, jwt
from django.conf import settings
def generate_access_token(user):
access_token_payload = {
'user_id': user.id,
'exp': datetime.datetime.utcnow() + datetime.timedelta(
days=0, minutes=60
),
'iat': datetime.datetime.utcnow(),
}
access_token = jwt.encode(
access_token_payload,
settings.SECRET_KEY, algorithm='HS256'
).decode('utf-8')
return access_token
def generate_refresh_token(user):
refresh_token_payload = {
'user_id': user.id,
'exp': datetime.datetime.utcnow() + datetime.timedelta(days=7),
'iat': datetime.datetime.utcnow(),
}
refresh_token = jwt.encode(
refresh_token_payload,
settings.REFRESH_TOKEN_SECRET, algorithm='HS256'
).decode('utf-8')
return refresh_token
def jwt_login(response, user):
access_token = generate_access_token(user)
refresh_token = generate_refresh_token(user)
data = {
'access_token': access_token,
}
response.data = data
response.set_cookie(key="refreshtoken", value=refresh_token, httponly=True)
return response
'Back-End > Django' 카테고리의 다른 글
[Django] DRF 구조 이해 #1 (APIView) (0) | 2022.01.11 |
---|---|
[Django] django 기본 인증 시스템 커스텀(아이디, 이메일 로그인) (0) | 2022.01.03 |
[Django] DRF를 사용한 JWT Authentication #1 (0) | 2022.01.03 |
[Django] Nested Serializer - Create (0) | 2021.12.31 |
[Django] django-debug-toolbar 안보임 오류 해결 (0) | 2021.12.17 |