Dev/Django

Django 응용하기 - Authorization Decorator 만들고 활용하기

sincerely10 2020. 7. 19. 18:17
반응형

안녕하세요. 지난 포스트에서는 Python에서 Authentication&Authorization(인증&인가) 하는 내용을 기재했습니다. 사실 Django 보다는 Python 내용에 가까웠지만 이 포스트가 중요하고 연계성이 있기 때문에 Django로 카테고리를 분류 했습니다.

웹 사이트를 이용할 때, 여러 기능이 있는데 우리는 이 기능을 매번 로그인 하지 않고 사용합니다. 이것은 지난 포스트에서도 말씀 드린 것 처럼 Front-end에서 Local Storage 또는 Session Storage를 이용해 Header에 access token을 달고 request를 보내기 때문입니다.

그리고 이 반복적인 인가 확인을 위해 Decorator를 구현해 인가가 필요한 API function 마다 적용합니다.

1. Authorization Decorator function 만들기

먼저 코드부터 확인해보겠습니다.

 1 import  jwt
 2 import  json
 3 import  requests
 4
 5 from django.http            import JsonResponse
 6 from django.core.exceptions import ObjectDoesNotExist
 7
 8 from my_settings            import SECRET_KEY
 9 from users.models           import User
10
11 def authorization_decorator(func):
12     def wrapper(self, request, *args, **kwargs):
13         try:
14             access_token    =   request.headers.get('Authorization', None)
15             payload         =   jwt.decode(access_token,SECRET_KEY['secret'],algorithm='HS256')
16             login_user      =   User.objects.get(id=payload['id'])
17             request.user    =   login_user
18
19         except jwt.exceptions.DecodeError:
20             return JsonResponse({'message':'INVALID_TOKEN'},status=400)
21
22         except User.DoesNotExist:
23             return JsonResponse({'message':'INVALID_USER'},status=400)
24
25         return func(self, request, *args, **kwargs)
26
27     return wrapper

Line 1-3: 관련한 python module을 import 해줍니다.

Line 5-6: django에서 필요한 module을 import 해줍니다. 해당하는 ueser가 없을 때를 위해 예외처리 관련한 ObjectDoesNotExist module도 있음을 기억해주세요.

Line 8-9: Secret Key가 저장되어 있는 별도의 파일 my_settings.py와 회원정보의 Model로 데이터를 컨트롤 해야 하므로 User App의 models.py 파일을 import 합니다.

Line 11-12: 일반적으로 사용하는 Decorator와 비슷한 형태로 선언합니다. wrapper의 형태는 만드는 API function과 비슷하게 정의 해줍니다. 저 같은 경우는 API function에 self(객체 자신)과 request를 받게 해놓았습니다. 활용도가 있으므로 *args와 **kwargs도 Arguments로 받아줍니다.

Line 14: request에 있는 Header에 Authorization key를 주어 token value를 받아 옵니다. 만약 header에 값이 없다면 None이 return될 것입니다.

Line 15: jwt의 decode method로 복호화를 진행합니다. 제가 import한 secret_key가 필수입니다. 이 때, access_token의 값이 Null이거나 decode가 되지 않는다면 error가 발생하고 19번 라인으로 예외처리가 됩니다.

Line 16: {"id":15}와 같은 형태인 payload를 가지고 User 객체를 받아옵니다. 저는 이를 login_user라 지칭하였습니다. 만약 DB에 해당하는 user가 없는 경우 22번 라인으로 예외처리가 됩니다.

Line 17: request라는 클래스에 user라는 변수를 지정해 특정 객체를 저장합니다. 이 객체가 실질적으로 API function에 쓰일 값 입니다.

2. Authorization Decorator 활용하기

조금 전 만든 인가 데코레이터 정확히는 API Decorator를 사용하는 API에 붙여 보겠습니다. 예시 코드로 확인해보겠습니다.
저는 Following이라는 User 객체가 다른 User를 Follow 하는 API에 적용 해보았습니다.

 1 import json
 2
 3 from django.http    import JsonResponse
 4 from django.views   import View
 5
 6 from users.models   import User,Following
 7 from my_settings    import SECRET_KEY
 8 from utils          import authorization_decorator
 9 
10 class FollowUserView(View):
11
12     @authorization_decorator
13     def post(self,request):
14         data    =   json.loads(request.body)
15         user_id =   request.user.id
16
17         try:
18             if User.objects.filter(id=data['followed_user_id']).exists():
19                 following_users = list(Following.objects.filter(followed_user=data['followed_user_id']).values('following_user_id'))
20
21                 for following_user in following_users:
22                     if user_id  ==  following_user['following_user_id']:
23                         return JsonResponse({'message':'Already Followed'}, status=401)
24
25                 Following(
26                         followed_user_id     =   data['followed_user_id'],
27                         following_user_id    =   user_id
28                 ).save()
29                 return JsonResponse({'message':'SUCCESS'}, status=200)
30
31             return JsonResponse({'message':'INVALID_USER'}, status=401)
32
33         except KeyError:
34             return JsonResponse({'message': 'KEY_ERROR'}, status=400)

우선 관련한 모듈을 import 해줍니다. posts라는 App의 views.py에는 직접적으로 jwt를 사용하지 않기 때문에 import 하지 않았습니다. 가장 중요한 것은 8번 Line에 제가 작성한 authorization_decorator 함수를 import 하는 것이겠네요.

제가 구현한 API에는 Following을 하는 유저(following_user) 즉, 로그인 해서 행위를 하는 user는 'access token'을 통해 받아 오기로 하였습니다. 저에게만 국한되는 것이 아니라 로그인 한 유저가 특정 user를 Following 하는 것이 당연한 과정입니다.
그리고 Following을 당하는 유저(followed_user) 즉, 로그인한 유저에 의해 팔로우 요청을 받은 유저는 request의 Body를 통해 받아오기로 하였습니다.

Line 12: impact가 빠져 있는데 Decorator로 FollowUserView라는 클래스의 post 함수를 꾸며줍니다.

Line 15: decorator를 통해 받아온 request에 user id를 받아옵니다. 저는 user_id라는 변수로 id만 받았습니다.

Line 18: 먼저 팔로잉 요청을 받는 유저가 존재하는지 확인해줍니다.

Line 19: 조금 복잡해 보일 수도 있는 로직인데 follow 요청을 받는 유저의 기존 following 된 List 자료형으로 받아옵니다. 중복 following이 말이 되지 않기에 이미 follow 하고 있다는 것을 알려주기 위함입니다. DB의 무결성을 위한 것이기도 하죠.

Line 21 - 23: 이미 following 된 경우를 for문으로 찾습니다. header에 있는 user가 있는지 확인하고 있다면 response를 보내줍니다. 이미 Follow 하고 있다는 메세지와 함께요.

Lime 25 - 29: 기존에 Following을 하고 있지 않다면, Following이라는 model 객체에 저장해줍니다.

여기 까지가 decorator를 통해 Following View를 구현한 내용입니다. 실제 view에서는 15번 라인 한 줄로만 사용되어 굉장히 간결하게 느껴집니다.

중요한 것은 Decorator를 구현함으로써 구현할 많은 App에서 인가를 하나하나 구현하지 않아도 된다는 것 입니다.

저는 더 좋은 포스트로 돌아오겠습니다!

반응형