[TIL. 39] 토큰 확인을 위한 decorator 함수

신지원·2021년 4월 10일
0
post-thumbnail

인가를 위한 데코레이터 함수 구현하기

인가를 위해 token decode를 위해 필요한 데코레이터 함수

💡 데코레이터 함수란?
함수를 받아 명령을 추가한 뒤 이를 다시 함수의 형태로 반환하는 함수이다.
함수의 내부를 수정하지 않고 기능에 변화를 주고 싶을 때 사용한다.
일반적으로 함수의 전처리나 후처리에 대한 필요가 있을때 사용을 한다.
또한 데코레이터를 이용해, 반복을 줄이고 메소드나 함수의 책임을 확장한다.

데코레이터를 실행하려면 @를 붙여서 사용한다.

import jwt

from django.http import JsonResponse

from my_settings import SECRET
from user.models import User

def login_check(func):

    def wrapper(self, request, *args, **kwargs):

        try:
            token = request.headers.get('Authorization', None) 
            payload = jwt.decode(token,SECRET['secret'], algorithms='HS256')
            user = User.objects.get(id=payload['id'])   
            request.user = user   

        except User.DoesNotExist:
            return JsonResponse({'MESSAGE': 'INVALID USER'}, status=400)

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

        return func(self, request, *args, **kwargs)

    return wrapper

내가 더 잘 이해하기 위한 설명

1. 로그인시 발행한 토큰을 받아온다. (token)

request라는 객체의 headers라는 attribute가 있다.
attribute인 headers는 딕셔너리 data type으로 되어 있고, Authorization의 value를 가져오고 없을 경우, none을 반환한다.

class Request():
	headers = {'Authorization':
    'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6NX0.j00KJWq4J-rKywhLifGz3Sz4wVIOv7bsXLfLesfUIJg'}

즉, 이런 모습으로 headers에는 이렇게 로그인시 발행받은 토큰을 value값으로 가지고 있다. (제가 가상으로 만든 토큰입니다.)

2. 받아온 토큰을 다시 decode한다. (payload)

encode되어 있는 토큰을 decode해서 payload에 넣어준다. 이때, secret_key는 encode해준것과 같아야 한다.

3. 사용자 식별하기 (user)

decode한 토큰을 user앱의 model에 있는 User라는 테이블에서 id가 같은 사람을 찾아서 사용자가 누구인지 식별한다.

4. 객체의 attribute (request.user)

request라는 객체의 user라는 attribute 만들어서 user.id인 user 정보를 담았다.

=> 이렇게 만든 데코레이터 함수는 게시글을 포스팅할때도 사용할 수 있다.

게시글 포스팅

# model.py
from django.db import models

class Feed(models.Model):
    uesr       = models.ForeignKey('user.User', on_delete=models.CASCADE)
    created_at = models.DateTimeField(auto_now_add=True) # 처음 올릴때 시간
    updated_at = models.DateTimeField(auto_now=True)  # 게시글 수정하면 생기는 시간
    images     = models.CharField(max_length=500)

    class Meta:
        db_table = 'feeds'

정말 간단하게 테스트 해볼려고 사진 하나만 올라가게 만든 모델...!

#views.py
import json

from django.http  import JsonResponse

from django.views import View
from feed.models  import Feed
from utils        import login_check

class PostfeedView(View):
    @login_check
    def post(self, request):
        try:
            data = json.loads(request.body)

            Feed.objects.create(
                user    = request.user,
                images  = data['images'],
            )
            return JsonResponse({'MESSGAGE': 'SUCCESS'}, status=201)

        except KeyError:
            return JsonResponse({'MESSGAGE': 'NO PICTURE'}, status=400)

    @login_check
    def get(self, request):
        result = []

        contents = Feed.objects.filter(user=request.user)  
        for content in contents:
            result.append(
                {
                    'images' : content.images,
                }
            )
        return JsonResponse({'MESSAGE': result},status=201)

@login_check 를 미리 실행했기 때문에 유저에게 발행해준 토큰을 decode했기때문에 어떤 사용자가 사진을 올렸는지를 확인 할 수있다. 이것을 데이터 베이스에서 확인해보면 다음과 같다.
이렇게 user_id를 통해 어떤 사용자가 사진을 업로드 했는지 확인이 가능하다.

헷갈렸던것

contents = Feed.objects.filter(user=request.user)

model을 다시 보면 user,created_at,updated_at, images 모두 Feed라는 객체의 attribute이다.
이 attribute들의 data type은 DateTimeField이거나, CharField이다.
그런데 user는 FK로 연결하고 있기 때문에 object이다.

contents = Feed.objects.filter(user=request.user)

여기서 user는 객체이기 때문에 똑같이 객체를 비교해주어야 한다.(객체는 객체와 비교해야 한다.)

데코레이터 함수에서 객체인 request의 attribute로 user을 넣어준 것도 이 이유이다. 만약 그냥 단순히 객체의 속성값으로 user을 넣어주지 않고 바로 attribute인 user(데코레이터 함수에서)을 user = user로 넣어주면 앞에 있는 user는 객체이지만 뒤에 데코레이터함수의 user는 attribute이기 때문에 맞지 않는다. 따라서 데코레이터 함수를 만들때 request.user를 만든 것이다.

0개의 댓글