1.클라이언트는 LoginApi를 호출하면서 {"username": "이름", "password": "비번"} 정보를 전달해준다.
2.서버는 username과 password를 가지고, 해당하는 유저를 찾은 다음 jwt_login을 수행한다.
3.jwt_login에서는 access_token과 refresh_token을 생성한다.
4.생성된 access_token은 {"access_token": access_token}형태의 json으로 클라이언트에 전달되고, 생성된 refresh_token은 httpOnly=True 속성을 가진채로 cookie에 삽입된다.
1.클라이언트에서 LogoutApi를 호출한다.
2.호출과 동시에 클라이언트는 가지고 있던 access_token을 삭제한다.
3. 서버에서는 cookie에 존재하는 refreshtoken을 삭제한다.
1.클라이언트에서 RefreshJWTtoken을 호출한다.
2.서버는 cookie에 있는 refreshjwttoken을 읽어서 decode한다.
3.decode한 정보로 유저를 추출하고, 로그인 할 때와 마찬가지로 access_token을 새로 만들어서 json 형태로 전달한다.
## 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
## 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