[Django] Westagram - Login Decorator

haejun-kim·2020년 8월 15일
4

[Django]

목록 보기
17/20
post-thumbnail

hash를 사용하여 비밀번호를 암호화하고, JWT를 통해 권한에 대한 토큰을 발행시켰다. 그 후에 생성한 기능이 바로 로그인 데코레이터다. 로그인 데코레이터는 왜 필요할까?

이 물음에 대한 답을 하기 위해서는 HTTP의 가장 큰 특징인 Stateless에 대해서 이해해야한다. Stateless는 상태를 저장하지 않는다는 특징이다. 즉, request와 response로 통신을 할 뿐 그 전에 통신한 내용에 대해서는 전혀 저장하고 있지 않다. 이 특징을 이해하고 다음 예시를 보자.

내가 어떤 웹 사이트에서 글을 쓰고 싶다. 근데 글을 쓰기 위해서는 로그인을 한 유저라는 사용자 권한이 필요하다. 이 권한을 우리는 access token을 발행하고, 그 토큰을 프론트엔드와 서버가 서로 통신하며 이 사용자가 권한을 가지고 있는 유저인지 아닌지에 대해서 판별한다. 이 때 웹사이트는 토큰을 주고 받은 내용을 전혀 저장을 하지 않고있기 때문에 우리는 권한이 필요한 모든 페이지에서의 통신에 access token을 주고받고를 명시해줘야한다. 이걸 코드에서 작성을 하면 반복되는 코드가 계속 쓰이기에 비효율적이며, 가독성에도 좋지 않은 영향을 준다. 그래서 파이썬에서는 데코레이터라는 기능을 사용하여 이런 문제를 훨씬 손쉽게 해결한다.

Decorator

Decorator는 '장식하다' 라는 뜻으로 유추해보면 무언가를 장식하는 기능을 하는것이라고 유추해 볼 수 있다. 그럼 무엇을 장식할까? 바로 함수를 장식한다. 기존에 있던 A 라는 함수에 B라는 함수를 데코레이팅하면 B 함수의 기능을 먼저 수행하고 A라는 함수의 기능을 수행한다. 이러한 기능때문에 주로 공통적으로 항상 먼저 실행되어야 하는 코드가 있을 때 이 기능을 사용한다.

데코레이터가 가장 범용적으로 사용되는 예는 다음과 같다.

어떤 웹 사이트에서 게시글을 작성하기 위해선 로그인을 한 유저라는 권한이 필요할 때, 권한을 확인하는 ( 토큰을 가지고 있는 유저인지 확인하는 ) 함수를 한번 만들어 놓으면, 권한을 확인하는 기능이 필요할 때마다 해당 함수를 데코레이팅 해주기만 하면 토큰을 가지고 있는지 확인하는 작업을 먼저 수행하기 때문에 훨씬 가독성도 좋고 효율성도 좋은 코드를 작성할 수 있다.

사용하는 방법은 간단하다. decorator 사용하는 함수는 함수의 이름 앞에 @를 붙여서 적용하고자 하는 함수 정의 부분 위에 지정해주면 된다.

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

기본적으로 로그인 인증 데코레이터가 왜 필요한지, 그리고 데코레이터가 무엇인지에 대한 설명을 했다. 그렇다면 이제 어떻게 사용하는지를 알아보자.

로그인 데코레이터는 User App에 utils.py파일을 생성해서 그 안에 코드를 작성하는것을 추천한다.

우리는 이 데코레이터를 만들어 사용자가 특정 기능을 수행하기 전에 사용자가 권한이 있는지 살펴볼 것이다. 만약 토큰 권한이 확인되지 않는다면 특정 기능에 대한 함수는 실행되지 않는다.

먼저 필요한 모듈을 import 해야한다.

import jwt, json, requests

from functools              import wraps
from django.http            import JsonResponse
from django.core.exceptions import ObjectDoesNotExist

from westagram.settings import SECRET_KEY
from .models            import User
  1. 데코레이터의 특징은 데코레이터를 사용하면 내장 함수의 속성을 잃어버린다는 것이다. 이러한 문제점을 방지하기 위해서 데코레이터를 사용할 땐 필수적으로 wraps를 사용하자. 이것을 사용하기 위해 임포트해주었다.
  2. DB에 요청에 대한 값이 없을 경우에 대한 예외처리를 하기 위함
  3. 토큰을 요청 받았을 경우 그에 대한 처리가 필요하기 때문에 디코딩을 해야한다. 디코딩을 할 경우 SECRET KEY가 필수적으로 필요하다. 장고의 기본적인 settings.py에 SECRET KEY가 있을 경우에는 임포트 할 필요없이 그냥 사용해주면 된다. 하지만 필자는 보안을 위해서 SECRET KEY를 새로운 파일을 만들어서 따로 저장해두었기 때문에 임포트해주었다.

필요한 모듈을 모두 임포트했기 때문에 이제 코드를 구현하면 된다.

def login_required(func):
    @wraps(func)
    def wrapper(self, request, *args, **kwargs):
        try:
            access_token = request.headers.get('Authrozation', None)
            payload      = jwt.decode(access_token, SECRET_KEY, algorithms = 'HS256')
            user         = User.objects.get(email = payload['email'])
            request.user = user

        except jwt.exceptions.DecodeError:
            return JsonResponse({'message' : 'INVALID TOKEN'}, status = 400)

        except User.DoesNotExist:
            return JsonResponse({'message' : 'THIS ACCOUNT DOES NOT EXIST'}, status = 400)

        return func(self, request, *args, **kwargs)
    return wrapper
  1. 위에서 설명한 내장 함수의 속성을 잃어버리지 않게 하는 wraps는 내장함수 위에 선언해주면 된다.
  2. access token은 HTTP request의 header에서 값을 보내기 때문에 우리는 header의 값을 get해줘야한다. 그리고 Authorization의 값을 가져오고, 값이 없다면 None으로 넘긴다.
  3. payload는 디코딩을 하면 나오게 될 사용자에 대한 정보를 담을 변수. 여기서 SECRET KEY와 algorithms에 대한 정보는 토큰 발행시 넣었던 정보와 일치해야한다.
  4. 디코딩 해서 나온 사용자에 대한 정보와 매칭되는 사용자 정보를 불러오고, user라는 변수에 저장한다.
  5. Request에 user 변수를 저장해 데코레이터 다음에 나오는 함수에서 사용한다. 토큰 정보를 확인하는 HTTP Requeset에는 토큰을 제외하고는 사용자 정보가 들어오지 않기 때문에 user변수에 값을 저장한다.
  6. 토큰 값이 없을 경우의 예외처리
  7. 유저정보가 DB에 없을 경우에 대한 예외처리

이제 로그인 권한이 필요한 함수의 윗부분에 작성한 데코레이터를 임포트해서 사용해주기만 하면 된다!

0개의 댓글