TIL - jwt Token Checker

Heechul Yoon·2020년 2월 17일
1

LOG

목록 보기
9/62

로그인을 해서 받은 토큰을 확인하는 loger을 만들어보자.

우선 로그인이 필요한 서비스를 정의한다.

이번에는 댓글페이지에 접근해서 로그인이된 유저(로그인해서 토큰을 발행받은유저)만 댓글을 달 수 있는 기능을 만들어보겠다.

우선 유저는 endpoint로 name(유저아이디), content(댓글)을 바디에, token정보를 headers에 첨부해서 보낼 것이다.

#replys/view.py
class ReplyView(View):
      @Authentificator                                    
      def post(self, request):
          data = json.loads(request.body)
  
          Reply(
                name = data['name'],
                content = data['content']
                  ).save()
  
          return HttpResponse(status = 200)

request는 경로를 따라 ReplyView클래스의 post매서드로 온다. 그리고 post매서드를 작동시키기 전에 Authentificator이라는 데코레이터로 이동한다.

def Authentificator(func):                                                               # 데코레이터가 걸린 매서드를 arrument로 가져옴. 즉, func함수를 파라미터랑 싹다같이 가져옴
    def wrapper(self,request, *args, **kwargs):                                          # 조건이 되는 부분을 wrapper함수로 감싼다.(여기서 해당 매서드의 func 파라미터 싹다가져옴, 클래스니깐 self도 가져옴, 들어온 request랑 다른 요청 다가져오기위해 *args, **kwargs해준다.) 

        requested_token = request.headers.get('Authorization', None)
        # header에서 Authorization 값을 가져옴. header은 body와다르게 json이 아님. header가 어떻게 들어오는지 request객체를 열어봐야함. request객체안에 header값을 get해서 Authorization키가 있으면 가져오고 >없으면 None을 반환. try except처리 안해줘도 되서 편한 매서드
        encode_token = requested_token.encode('utf-8')
        decrypted_token = jwt.decode(encode_token, 'wecode', algorithm = 'HS256')        # 가져온 Authorization값을 jwt로 decode해주어 유저정보를 얻어냄
        try:
            if Account.objects.filter(name = decrypted_token['name']).exists():          # 만약 decode해서 읽어온 유저정보가 db에 exist하면 
                return func(self,request, *args, **kwargs)                               # arrument로 가져온 func()를 리턴함, 가져올때 다가져왔으니 리턴도 싹다해줌.


            return JsonResponse({'message' : 'INVALID_TOKEN'}, status = 403)             # 값이 다르거나 없는경우는 메시지와 403을 return

        except InvalidSignatureError:
            return JsonResponse({'message' : 'IVALID_TOKEN'}, status = 401)
    return wrapper

Authentificator데코레이터는 accounts 즉 유저정보와 관련되기 때문에 유저데이터 정보가 담긴 accounts app에 파일로 저장해둬 필요시 범용적으로 사용되도록 한다.

이 데코레이터에서 주목할 점은 replys/reply(댓글을 담당하는 endpoint)로 온 request를 어떻게 데코레이터의 parameter로 가져오느냐 이다.

코드가 request를 처리하는 순서대로 타고가면서 분석해보자.

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

Authentificator란 decorator의 parameter로 ReplyView를 가져온다. 그리고 wrapper라는 함수를 만들어 ReplysView의 request를 가져온다. request에 어떤게 담겨져 올지 모르기 때문에 모든 인자를 받는다는 의미에서 *args와 **kwargs를 parameter로 넣어주고, Authentificator함수 또한 ReplyView클래스 안의 decorator로 작용되는 함수이기 때문에 self parameter또한 받아준다.

requested_token = request.headers.get('Authorization', None)

이제 request의 헤더값에 첨부된 Authorization값을 불러올 차례이다. request의 객체 headers안에 Authorization이라는 key를 호출해서 value를 불러오는 매서드이며, 많약 value값이 없으면 None을 가져온다. None을 value로 가져옴으로 인해서 value가 없을 때 발생할 error처리를 할 필요가 없게 해주는 것이 핵심이다.

그렇다면 request의 header는 어떻게 구성될까?


위의 사진은 메인코드를 전부 주석처리하고 request.headers 객체 자체를 리턴한 값이다. content-length, content-type등 여기서 언급된 headers를 구성하는 값들의 key가 담겨져왔다.


위의 사진은 request.headers.values()를 통해서 value값을 전부 리턴한 것이다. 각각의 key에 해당하는 value들이 구분없이 나열되어 있다.

다시 본론으로 넘어가서

encode_token = requested_token.encode('utf-8')
        decrypted_token = jwt.decode(encode_token, 'wecode', algorithm = 'HS256')

request.headers.get('Authorization',None)으로 가져온 토큰값(string type)을 비교하기위해 byte type으로 encode해준다. 그리고 encode한 토큰, secretkey, 알고리즘종류를 인자로 입력하여 토큰을 디코드 시켜 유저 정보를 얻어낸다.(이 유저정보는 토큰을 만들 때 여기서넣었던 name)

이제 얻어낸 유저name의 값을 db에서 비교해야한다.

if Account.objects.filter(name = decrypted_token['name']).exists():         
                return func(self,request, *args, **kwargs)                         
            return JsonResponse({'message' : 'INVALID_TOKEN'}, status = 403)

얻어낸 유저정보(name)에 해당하는 value값이 데이터베이스에 있으면 로그인된 유저이기 때문에 그대로 다음 로직의 함수인 ReplyView를 실행해서 리턴해준다. 물론 처음에 데코레이터로 들어올 때 받아왔던 self, request, *args, **kwargs를 parameter로 넣어 똑같이 리턴해준다.

마지막으로 발생되는 error를 잡아줄 차례이다.

except InvalidSignatureError:
            return JsonResponse({'message' : 'IVALID_TOKEN'}, status = 401)
    return wrapper

except로 발생할 수 있는 error들을 처리해주고 wrapper함수를 리턴한다.

profile
Quit talking, Begin doing

0개의 댓글