django- pyJWT Login Decorator

김현우·2020년 8월 15일
1

django

목록 보기
2/6

오늘은 decorator에 대하여 알아보겠습니다. 🙂

데코레이터는 ,

함수를 받아 명령을 추가한 뒤 이를 다시 함수의 형태로 반환하는 함수입니다.

장점으로는 ,

함수의 내부를 수정하지 않고 기능에 변화를 주고 싶을 때
반복을 줄이고 메소드나 함수의 책임을 확장 가능하다는 점!!


데코레이터의 구조를 먼저 살펴보죠.🤔

아래와 같습니다.

def out_func(func):
    
    def inner_func(*args, **kwargs):

        return func(*args, **kwargs)
    
    return inner_func

example )

def decorator(func):
    def wrapper(*args, **kwargs):
       
        print('출력 1')   
        print(func(*args, **kwargs))
        print('출력 3')
    return wrapper
@decorator
def example():
    return '출력 2'
    
# 출력 순서
example()
출력 1
출력 2
출력 3

JWT는 ,

JavaScript를 이용해 유저가 웹사이트에 로그인 한 후에 발생하는 이벤트(댓글, 게시판 글쓰기 등), 여러가지 서비스를 이용하기 위한 권한을 가진 유저인지 확인하기 위해서 많이 쓰입니다.

사용자가 로그인을 한후에 jwt를 통해 token을 발급하는데 토근을 활용하는 이유는 http통신의 stateless때문!!


🤔왜 써야할까요?🤔

웹사이트에서 댓글, 게시판, 글쓰기 등등 서비스를 이용할때 로그인된 유저인지, 이용가능한 유저인지 확인하는 과정이 필요하기 때문입니다!!


🤖로직이 어떻게 되는가?🤖

로그인을 하게되면, 프론트엔드에게 해당되는 access_token을 전달하게 됩니다.

토큰 발행시, 토큰을 byte형식이 아닌 str형식으로 해야 하는데 그 이유는 http통신으로 전달할때 byte형식은 전달이 안되기 때문!!

  1. 서비스를 이용할 때, 권한을 확인하기 위하여 프론트엔드에서 HTTP 헤더를 통해 토큰을 전달 받는다.

  2. jwt토큰은 byte타입이므로 jwt토큰을 str타입으로 decode한다.

  3. access토큰을 발행했을 때 아래와 같이 만들었는데,

access_token = jwt.encode({'id' : 1}, SECRET, algorithm = 'HS256')

이때 첫 부분, 즉 {'id' : 1}정보를 바탕으로 database의 id와 access_token의 정보를 비교하여 준다.
4. 토큰이 일치할 경우, request할 객체에 프론트엔드에게 전달한다.


👏구현해보기!!👏

def login_deco(func):
    def wrapper(self, request, *args, **kwargs):
        try:
            access_token = request.headers.get('Authorization', None)         
            payload = jwt.decode(access_token, SECRET_KEY, algorithm='HS256')  
            
            account = Account.objects.get(name= payload['name'])                 
            request.account = account                                          
        except jwt.exceptions.DecodeError:                                     
            return JsonResponse({'message' : 'INVALID_TOKEN' }, status=400)
        except Account.DoesNotExist:                                        
            return JsonResponse({'message' : 'INVALID_USER'}, status=400)

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

이미 로그인이 된 상태에서도 어떠한 서비스에 접근 할때에는 권한이 필요하다.
따라서 내가 구현했던 인스타그램에서는 posting과 comment부분에 필요하다!

@login_deco 참고!!


👏전체 구현 코드👏

class PostingView(View): 
    @login_deco
    def post(self, request):
        try:
            data       = json.loads(request.body)
            user       = data['user']
            content    = data['content']
            img_url    = data['img_url']

            get_user = Account.objects.get(name = user)
            Posting(
                user       = get_user,
                content    = content,
                img_url    = img_url,
            ).save()
            return JsonResponse({'message': 'SUCCESS22'}, status = 200)
        except KeyError:
            return JsonResponse({'message': 'KEY_ERROR'}, status = 400)
        
        # DoesNotExist는 get에서 조건에 맞는 data가 없을 경우 발생됨. filter의 경우 [] return
        except Account.DoesNotExist:
            return JsonResponse({'message': 'Account_DoesNotExist'}, status = 401)    
        except json.decoder.JSONDecodeError:
            return JsonResponse({'message': 'JSON_TYPE_Error'}, status = 401)
        except ValueError:
            return JsonResponse({"message": "VALUSE_ERROR"}, status = 400)

    @login_deco
    def get(self, request):
        posted_data = Posting.objects.values()
        # print(list(posted_data))
        return JsonResponse({'message': list(posted_data)}, status = 200)

class CommentView(View):
    @login_deco
    def post(self, request):
        try:
            data = json.loads(request.body)
            comment_user            =   data['user']
            comment_content         =   data['content']
            post_comment_id         =   data['post']  # post_id는 database에서 post의 id와 동일하게

            user_name               =   Account.objects.get(name = comment_user)
            post_id                 =   Posting.objects.get(id = post_comment_id)
        except KeyError:
            return JsonResponse({'message': 'KEY_ERROR'}, status = 400)
        except Account.DoesNotExist: 
            return JsonResponse( {'message': 'Account_DoesNotExist'}, status = 401)
        except Posting.DoesNotExist:
            return JsonResponse( {'message': 'Post_DoesNotExist'}, status = 401)
        except json.decoder.JSONDecodeError:
            return JsonResponse({'message': 'JSON_TYPE_Error'}, status = 401)
        except ValueError:
            return JsonResponse({"message": "VALUSE_ERROR"}, status = 400)
        Comment(
            user    = user_name,
            post    = post_id,
            content = comment_content
        ).save()
        
        return JsonResponse({'message':'COMMENT_SUCCESS'}, status=200)

    @login_deco
    def get(self, request):
        try:
            if(Comment.objects.filter(post_id = "1").exists()):
                selected_data = Comment.objects.filter(post_id = "1").values()
                print(selected_data)
                if not selected_data:
                    return JsonResponse({'message': 'NO_COMMENT'})
                return JsonResponse({'message': list(selected_data)}, status = 200)
            else:
                return JsonResponse({'message': 'POST_NOT_EXIST'}, status = 401)
        except Comment.DoesNotExist:
            return JsonResponse( {'message': 'Comment_NOT_EXIST'}, status = 401)
profile
코딩을 잘하는 개발자가 되자!

0개의 댓글