Django Assignment 1 | Westagram 10)

김기현·2022년 2월 26일
0
post-thumbnail

인스타그램 클론 초기의 api에는 이메일과 비밀번호를 입력해 로그인을 하지만 팔로우, 코멘트, 포스팅 달 때 유저를 확인하는 별다른 확인의 과정없이 데이터를 저장하고 GET요청을 하면 DB의 자료를 보여주는 등의 기능을 구현하였습니다. 그 후에 인증 및 인가를 위해 각 view에 토큰을 가지고 유저를 확인한 후에 해당 api를 실행하도록 하였지만, 각 뷰마다 짧지 않은 코드를 계속 반복하는 일은 비효율적으로 보입니다.

JWT Decorator 구현

각 뷰마다 JWT를 구현하는 일은 비효율적으로 보인다고 했는데, 그러면 JWT를 인증하는 코드는 어디에, 어떻게 구현해야 할까요..?

답은... 엔드포인트에 데코레이터를 구현해야 하며 데코레이터 구현은 보통 user app에 utils.py를 만들어 구현합니다!

데코레이터 구현의 과정은 다음과 같습니다. utils.py에 토큰 디코드 과정을 거치는 코드를 넣고 각 뷰에 데코레이터로 해당의 기능을 덧붙입니다.

로그인 인증 데코레이터 만들기

로그인 인증 데코레이터를 사용할 함수에 공통적으로 적용하기 위해 보통 core앱이라는 따로 만들고 utils.py에 적용합니다. 아래의 예시는 user 앱의 utils 파일에 바로 만들었습니다.

  • utils.py
# users/utils
import json, bcrypt, jwt

from django.http  import JsonResponse
from django.views import View

from users.models     import User, Follow
from users.validators import validate_email, validate_password
from secret           import ALGORITHM
from config.settings  import SECRET_KEY

필요한 모듈을 import합니다. 토큰 디코딩에 사용할 시크릿 키와 알고리즘은 보안을 위해 따로 보관한 후 import합니다.

def login_decorator(func):
	# login_decorator를 실행하고 나서 수행할 api function을 파라미터로 받고
    def wrapper(self, request, *args, **kwargs):
    # self(instance 자신), request(HTTP request), 확장성을 고려해 *args, **kwargs까지 파라미터로 받습니다.
        try:
            if not 'Authorization' in request.headers:
            # 토큰을 보내주는 HTTP request의 헤더에 Authorization이 없으면
                return JsonResponse({"message" : "No Authorization in headers"})
                # 해당 에러를 response
            
            access_token   = request.headers.get("Authorization")
            # HTTP header에 Athorization을 가져와 access_token에 저장

            payload = jwt.decode(access_token, SECRET_KEY, ALGORITHM)
            # 가져온 토큰을 복호화 해 payload라는 변수에 넣습니다.
            user_id = payload['user_id']
            # 그 후에 사용자가 입력하는 user_id를 디코딩해 user_id에 넣습니다.
            # 왜냐면 로그인 시 발급했던 jwt에서 payload에 id를 저장했고, 해당 변수에서 id를 가져오기 위함
            

            if not User.objects.filter(id = user_id).exists():
            # 사용자가 입력한 user_id가 일치하지 않다면 response를 보냅니다.
                return JsonResponse({"message" : "INVALID_UESR"}, status = 401)
            
            # 그리고 위에서 저장한 user_id를 가져와 해당 변수에 저장
            user         = User.objects.get(id = user_id)
            request.user = user
            # HTTP에서 받은 request에 데코레이터를 활용하는 함수에서 사용
            # 토큰을 확인하는 HTTP request에는 토큰을 제외하고 사용자의 정보가 안들어오기 때문에 'user'값을 저장해 활용
            
            return func(self, request, *args, **kwargs)
            # 해당 함수를 리턴합니다.

        except jwt.exceptions.DecodeError:
        # 복호화를 하는 과정에서 이 서버에서 발급한 토큰이 아니라면 해당 에러처리
            return JsonResponse({"message" : "INVALID_TOKEN"}, status = 400)

        except KeyError:
            return JsonResponse({"message" : "DECORATOR_KEYERROR"}, status = 400)
    return wrapper 
    # 중첩 함수인 wrapper를 return
profile
피자, 코드, 커피를 사랑하는 피코커

0개의 댓글