Decorator는 사전에 하고싶은 작업을 전처리하는 함수로서 본 함수가 실행되기 전에 미리 생성된 함수를 실행하므로 코드 재활용에 엄청난 도움이 된다.
만약 Decorator함수를 사용하지 않으면 우리는 권한이 필요한 각 View에 Token이 유효한지 확인하는 코드를 계속 작성해야 한다.
users app에 utils.py라는 새로운 파일을 만들어 Token을 decode하는 과정의 코드를 넣고 각 View에 Decorator 함수를 추가한다.
# users/utils.py
import json, jwt
from django.conf import settings
from django.http import JsonResponse
from users.models import User
def SignInDecorator(func):
def wrapper(self, request, *args, **kwargs):
try:
# get 메소드로 HTTP Request의 Header로부터 JWT를 받는다.
# Authorization 키를 통해 토큰을 받아야 되는데 키가 없으면 NONE type 자료형을 담는다
access_token = request.headers.get('Authorization', None)
# JWT를 발급할때 사용한 SECRET_KEY와 ALGORITHM으로 Token을 Decode한다.
payload = jwt.decode(access_token, settings.SECRET_KEY, settings.ALGORITHM)
# Decode한 JWT로부터 User ID를 알아내어 권한을 부여받은 User를 저장한다.
user = User.objects.get(id=payload['id'])
request.user = user
return func(self, request, *args, **kwargs)
# Token이 적합하지 않은 경우 INVALID_TOKEN 에러를 반환한다.
# (None type을 decode할 때 나오는 에러를 잡기 위해)
except jwt.exceptions.DecodeError:
return JsonResponse({'message' : 'INVALID_TOKEN'}, status=401)
# User가 존재하지 않은 경우 INVALIDE_USER 에러를 반환한다.
except User.DoesNotExist:
return JsonResponse({'message' : 'INVALID_USER'}, status=401)
return wrapper
토큰 정보를 확인하는 HTTP Request에는 토큰을 제외하고는 사용자 정보가 들어오지 않기 때문에 user값을 request.user에 저장해서 이후 활용한다.
앞서 구현한 Decorator 함수를 PostingView에 적용시켜 보았다.
class PostingView(View):
@SignInDecorator
def post(self, request):
try:
data = json.loads(request.body)
image_url = data['image_url']
content = data['content']
user = request.user
if not User.objects.filter(email=data['email']).exists():
return JsonResponse({'message':'INVALID_USER'}, status=401)
Posting.objects.create(
image_url = image_url,
content = content,
user = user.id
)
return JsonResponse({'message':'SUCCESS'}, status=201)
except KeyError:
return JsonResponse({'message':'KEY_ERROR'}, status=400)
@SignInDecorator
def get(self, request, posting_id):
if not Posting.objects.filter(id=posting_id).exists():
return JsonResponse({'message':'NON_POST'}, status=400)
posting = Posting.objects.get(id=posting_id)
# postings = Posting.objects.all()
results = []
results.append(
{
'user' : posting.user.email,
'image_url' : posting.image_url,
'content' : posting.content,
'created_at' : posting.created_at
}
)
return JsonResponse({'results':results}, status=200)
로그인을 하여 서버에서 주는 access_token을 활용하여 PostingView의 GET메소드를 실행시켜 확인해보았다.
Client가 request에 access_token을 첨부하여 보내면 Server는 이 access_token을 복호화하여 User의 id를 얻고, 이 id의 권한을 확인하여 충분한 권한을 가지고 있으면 요청을 처리하고, 그렇지 않다면 Error를 반환한다.