[Django] 파이썬 Decorator 클래스를 활용한 토큰 인증

송진수·2021년 7월 28일
1

Decorator

Decorator 클래스는 __init__ 메서드를 통해 적용할 함수/메서드를 저장하고, __call__ 메서드로 호출될 때의 동작(대체로 적용할 함수가 실행되기 전에 실행할 코드)를 정의한다.

class Decorator:
    def __init__(self, function):
        self.function = function
    
    # self 다음 들어가는 인자들은 Decorator 클래스가 적용될 함수에 입력될 인자들이 들어간다.
    def __call__(self, *args, **kwargs): 
        # Decorator가 적용될 함수를 호출하기 전에 실행될 코드들
        print("Hello World!")
        return self.function(*args, **kwargs)
        
        
@Decorator
def func1(*args, **kwargs):
	print(args, kwargs)
    

func2(1,2,3,4,text="Hi!")

# Hello World!
# (1, 2, 3, 4) {'text': 'Hi!'}

참고로 decorator를 함수로 이용하는 방법은 아래와 같다..

def decorator(function):
   
   def wrapper(request):
      print("Hello World!")
      return function(request)
   
   return wrapper
   
@decorator
def func2(request):
    print(request)
    
func2("Hi!")

# Hello World!
# Hi!
    

이런 구조를 활용한다면, django 뷰에 선언된 함수가 실행되기 전에 요청에 붙은 토큰이 유효한지 확인(ex.로그인이 되어 있는지)하거나, 권한이 있는 유저인지 판별하는 로직을 일일이 추가할 필요 없이, import해서 @decorator 형태로 붙일 수 있을 것이다.

로그인 Decorator 적용 뷰

# utils.py

class LoginDecorator:
    def __init__(self, function):
        self.function = function
    
    def __call__(self, request, *args, **kwargs):
        token = request.headers.get("Authorization", None)
        try:
            if token:
                token_payload = jwt.decode(token, SECRET_KEY, algorithms='HS256')
                
                # 유저 모델에서 인스턴스를 찾아 request의 속성으로 부여
                request.user = User.objects.get(id=token_payload['user_id'])

                return self.function(self, request, *args, **kwargs)

            return JsonResponse({"message":"NEED_LOGIN"}, status=401)
        
        except DecodeError:
            return JsonResponse({"message":"INVALID_USER"}, status=401)

        except User.DoesNotExist:
            return JsonResponse({"message":"INVALID_USER"}, status=401)

위 decorator를 적용하고, 로그인한(토큰을 부여받은) 사용자가 자신의 이름을 조회할 수 있는 간단한 뷰를 작성하여 decorator 작동 여부를 확인했다.

# views.py

from utils import LoginDecorator

class LoginView(View):
    @LoginDecorator
    def get(self,request):
        return JsonResponse({"YOUR_NAME":request.user.name}, status=200)
        

위 캡쳐와 같이 헤더에 Authorization: '부여받은 토큰' 을 붙여 GET 메소드로 HTTP 요청을 보낸 결과, 의도대로 작동하는 것을 확인하였다.

만약 토큰이 없을 경우 NEED_LOGIN, 틀린 형식의 토큰일 경우 INVALID_USER가 나오게끔 하였다(401 Unauthorized).

profile
보초

0개의 댓글