Django | 인스타그램 클론 코딩(5) - 로그인 인증 데코레이터

Sua·2021년 2월 10일
0

Django

목록 보기
12/23
post-thumbnail
post-custom-banner

💁‍♂️ 로그인 인증 데코레이터로 권한 확인

지난 시간에는 게시물 등록 기능을 구현하였습니다. 이번에는 권한이 있는 유저만이 서비스를 사용할 수 있는 인가 과정을 구현하겠습니다. 구체적으로 로그인한 유저만 게시물을 등록할 수 있도록 로그인 인증 데코레이터를 만들고, 게시물 기능에 적용해보겠습니다.

어떻게 권한을 확인할까?

웹 사이트를 이용할 때, 우리는 이 여러 기능을 매번 로그인 하지 않고 사용합니다. 사실, HTTP는 stateless한 특성이 있어서 이전의 요청과 응답을 기억하지 못합니다. 어떻게 웹사이트는 내가 로그인한 사용자인지 알 수 있을 까요?

그 비밀은 HTTP Request Headers에 있습니다. 백엔드에서 로그인에 성공한 사용자에게 토큰을 발행하면 프론트 엔드에서는 이 토큰을 Local Storage 또는 Session Storage에 담아둡니다. 그리고 토큰을 새로운 요청 때마다 헤더에 넣어서 보내주면, 로그인된 상태로 유지시켜줍니다. 이때 이 토큰이 유효한 토큰인지 확인하기 위해 로그인 인증 데코레이터를 사용합니다.

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

# user/utils.py  								[0]

import jwt
import json

from django.http import JsonResponse
from my_settings import SECRET, ALGORITHM
from user.models import User

def login_decorator(func): 							[1]
    def wrapper(self, request, *args, **kwargs): 				[2]
        try:
            access_token = request.headers.get('Authorization', None) 		[3]
            payload = jwt.decode(access_token, SECRET, algorithms=ALGORITHM)    [4]
            user = User.objects.get(id=payload['id']) 				[5]
            request.user = user 						[6]
        
        except jwt.exceptions.DecodeError: 					[7]
            return JsonResponse({'message':'INVALID_TOKEN'}, status=400)

        except User.DoesNotExist: 						[8]
            return JsonResponse({'message':'INVALID_USER'}, status=400)
        return func(self, request, *args, **kwargs)

    return wrapper

[0] : 장고에서는 보통 데코레이터를 utils.py라는 파일에 별도로 저장해서, 필요할 때마다 import해서 사용합니다. login_decorator 함수는 유저 정보와 관련이 있으므로 user 디렉토리 아래에 utils.py 파일을 만들어서 함수를 저장하였습니다.

[1] : login_decorator 함수는 데코레이터가 끝나고 나서 수행될 메인함수를 매개변수로 받습니다.

[2] : 내부 함수인 wrapper 함수에는 self(instance 자신), request(http request)를 매개변수로 받고, 이후 확장성을 고려하여 *args와 **kwargs까지 매개변수로 받습니다. / 매개변수가 고정되어 있지 않을 때, wrapper함수를 가변 인수 함수로 만듭니다.

[3] : access_token은 HTTP Request의 헤더인 Authorization의 값을 가져오고, 없으면 None으로 넘깁니다.

[4] : payload는 토큰을 디코딩하면 나오게 될 사용자에 대한 정보입니다. 토큰 발행 시와 동일한 사용자라면 동일한 payload가 반환됩니다. 디코딩에 들어가는 SECRETalgorithms은 토큰 발행 시 넣었던 정보와 같아야 합니다.

[5] : 데이터베이스에서 토큰의 사용자 정보와 매칭되는 사용자 정보를 불러오고, user라는 변수에 저장합니다. 토큰 발행 시 id라는 키로 사용자 정보를 저장했으므로, 불러올 때도 동일하게 id라는 키를 사용합니다.

[6] : request에 user라는 변수를 지정해 [5]에서 불러온 유저 정보 객체를 담습니다. 이 객체를 데코레이터 다음에 나오는 함수에서 사용합니다.

토큰 정보를 확인하는 HTTP Request 에는 토큰을 제외하고는 사용자 정보가 들어오지 않기 때문에, 이 user 값을 request에 저장해서 이후 활용하는 것입니다.

여기에서 request는 가변 객체입니다. 가변 객체이기 때문에 다른 객체 또는 변수 할당이 가능한 것입니다.

[7] : 토큰이 손상되었거나 전달된 토큰 값이 없을 때 DecodeError가 발생합니다. 즉, 해당 서버가 발급한 토큰이 아니라면(시크릿키와 알고리즘 방식이 다르다면) decode가 불가하므로 발생하는 에러입니다.

[8] : 토큰에서 디코드된 사용자 정보가 데이터베이스에 존재하지 않을 때 DoesNotExist가 발생합니다.

게시물 등록 기능에 로그인 인증 데코레이터 적용하기

# posting/views.py

import json
from json.decoder import JSONDecodeError

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

from posting.models import Posting, Image
from user.models    import User
from user.utils     import login_decorator  --> 추가

class PostingView(View):
    @login_decorator 						[1]
    def post(self, request):
        try :
            data = json.loads(request.body)
            user = request.user 				[2]

            content        = data.get('content', None)
            image_url_list = data.get('image_url', None)

            if not image_url_list: 				[3]
                return JsonResponse({'message':'KEY_ERROR'}, status=400)

            posting = Posting.objects.create(
                content = content,
                user    = user
            )

            for image_url in image_url_list:
                Image.objects.create(
                    image_url = image_url,
                    posting   = posting
                ) 

            return JsonResponse({'message':'SUCCESS'}, status=200)
        
        except JSONDecodeError:
            return JsonResponse({'message':'JSON_DECODE_ERROR'}, status=400)

[1] : post 함수를 실행하기 전에 로그인 토큰을 확인하도록 데코레이터를 추가하였습니다.

[2] : user 변수에는 request에 담긴 유저 정보가 담깁니다.

[3] : user에는 데코레이터를 통과했다면 반드시 값이 담기므로 KeyError 처리할 필요가 없습니다.

참고사이트
https://velog.io/@devmin/Django-decorator-login-token-basic
https://devvvyang.tistory.com/44?category=973523
https://livetodaykono.tistory.com/49
https://phin09.tistory.com/63

profile
Leave your comfort zone
post-custom-banner

0개의 댓글