Henu
개발냥발
Henu
전체 방문자
오늘
어제
  • 분류 전체보기 (411)
    • DevOps (52)
      • Kubernetes (19)
      • Docker (14)
      • AWS (3)
      • Nginx (4)
      • Linux (4)
      • ArgoCD (1)
      • CN (2)
      • NATS (0)
      • Git (5)
    • Back-End (30)
      • Django (18)
      • Spring (5)
      • JPA (1)
      • MSA (5)
    • CS (87)
      • SystemSoftware (20)
      • OS (25)
      • Computer Architecture (16)
      • Network (23)
      • Database (2)
    • Lang (21)
      • Java (9)
      • Python (4)
      • C# (8)
    • Life (12)
    • 블록체인 (2)
    • Algorithm (204)
      • BOJ (160)
      • 프로그래머스 (19)
      • LeetCode (4)
      • SWEA (1)
      • 알고리즘 문제 해결 전략 (8)
      • DS, algorithms (7)
      • Checkio (5)
    • IT (2)

블로그 메뉴

  • GitHub
  • 글쓰기
  • 관리자

공지사항

  • Free!

인기 글

태그

  • Kubernetes
  • BFS
  • DFS
  • boj
  • 프로그래머스
  • docker
  • django
  • Network
  • 백트래킹
  • 다이나믹 프로그래밍

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
Henu

개발냥발

Back-End/Django

[Django] DRF를 사용한 JWT Authentication #2

2022. 1. 3. 18:58
이번 포스팅에서는 JWT를 생성하고 로그인,로그아웃 기능을 구현하는 방법을 정리해 보려고 한다.

 

로그인 로직


  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을 삭제한다.

refreshtoken을 삭제함으로써 완전히 로그아웃을 할 수 있다.

 

 

access_token 추가 발급


  1. 클라이언트에서 RefreshJWTtoken을 호출한다.
  2. 서버는 cookie에 있는 refreshjwttoken을 읽어서 decode한다.
  3. 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
    'Back-End/Django' 카테고리의 다른 글
    • [Django] DRF 구조 이해 #1 (APIView)
    • [Django] django 기본 인증 시스템 커스텀(아이디, 이메일 로그인)
    • [Django] DRF를 사용한 JWT Authentication #1
    • [Django] Nested Serializer - Create

    티스토리툴바