Server는 로그인한 유저에게 JWT를 발급하고, 유저는 HTTP Request에 발급받은 JWT를 첨부해 Server에 보내주어 인가를 받는다. 인가를 통해 Westagram Post와 Comment의 삭제, 수정 기능을 구현해보자.
아래 코드는 User가 로그인이 필요한 기능을 사용하기 전 로그인 여부를 확인하는 Decorator를 구현한 코드이다.
# users.utils.py
def SignInDecorator(func):
def wrapper(self, request, *args, **kwargs):
try:
# HTTP Request의 Header로부터 JWT를 받는다.
access_token = request.headers.get('Authorization', None)
# JWT를 발급할때 사용한 SECRET_KEY와 알고리즘으로 Token을 Decode한다.
payload = jwt.decode(access_token, SECRET_KEY, algorithms='HS256')
# Decode한 JWT로부터 User ID를 알아내어 권한을 부여받은 User를 저장한다.
user = User.objects.get(id=payload['id'])
request.user = user
# Token이 적합하지 않은 경우 INVALID_TOKEN 에러를 반환한다.
except jwt.exceptions.DecodeError:
return JsonResponse({'message' : 'INVALID_TOKEN', 'Token':access_token }, status=400)
# User가 존재하지 않은 경우 INVALIDE_USER 에러를 반환한다.
except User.DoesNotExist:
return JsonResponse({'message' : 'INVALID_USER'}, status=400)
return func(self, request, *args, **kwargs)
return wrapper
Decorator 함수를 다시 살펴보면, 첨부받은 Token을 decode한 후에 request.user = user
를 통해 Request에 user에 대한 정보를 담아 보내는 것을 알 수 있다. 이를 통해, 호출된 View는 어떤 User가 호출한 건지 알 수 있다.
게시물과 댓글의 삭제나 수정은 그 게시물이나 댓글을 작성한 유저만 삭제하거나 수정할 수 있도록 해야한다. Token을 Decode해서 유저의 정보를 얻어 권한이 없다면 에러를 반환하도록 하자.
# postings/views.py
class PostView(View):
...
@SignInDecorator
def delete(self, request, post_id):
# 삭제하고자 하는 Post의 존재 여부를 확인
if not Post.objects.filter(id=post_id).exists():
return JsonResponse({"MESSAGE":"NO_POST"}, status=400)
# Post를 작성한 User가 삭제요청을 보낸 User인지 확인
if Post.objects.get(id=post_id).user != request.user:
return JsonResponse({"MESSAGE":"Unauthroized"}, status=403)
# 삭제 요청한 Post 삭제
Post.objects.filter(id=post_id).delete()
return JsonResponse({"MESSAGE":"DELETE"}, status=200)
request.user
에 삭제요청을 한 User의 정보가 담겨있다. 이를 삭제하고자 요청한 Post의 작성자와 비교해 다르다면 Unauthorizaed 에러를 반환한다.
# postings/views.py
class CommentView(View):
...
@SignInDecorator
def delete(self, request, comment_id):
if not Comment.objects.filter(id=comment_id).exists():
return JsonResponse({"MESSAGE":"NO_COMMENT"}, status=400)
if Comment.objects.get(id=comment_id).user != request.user:
return JsonResponse({"MESSAGE":"Unauthroized"}, status=403)
Comment.objects.filter(id=comment_id).delete()
return JsonResponse({"MESSAGE":"DELETE"}, status=200)
# postings/views.py
class PostView(View):
...
@SignInDecorator
def put(self, request, post_id):
try:
data = json.loads(request.body)
# 수정하고자 하는 Post 존재 여부 확인
if not Post.objects.filter(id=post_id).exists():
return JsonResponse({"MESSAGE":"NO_POST"}, status=400)
# Post를 작성한 유저가 수정요청을 보낸 유저인지 확인
if Post.objects.get(id=post_id).user != request.user:
return JsonResponse({"MESSAGE":"Unauthroized"}, status=403)
# Post 수정
Post.objects.filter(id=post_id).update(post = data['post'])
return JsonResponse({"MESSAGE":"SUCCESS"}, status=201)
except KeyError:
return JsonResponse({"MESSAGE":"KEY_ERROR"}, status=400)
수정하고자 하는 Post가 Database에 존재하는지 확인한 후, 존재하지 않는다면 NO_POST 에러를 반환한다.
Decorator를 통해 request.user에 수정요청을 한 User의 정보가 담겨있다. 이를 수정하고자 요청한 Post의 작성자와 비교해 다르다면 Unauthorizaed 에러를 반환한다.
적절한 권한을 가지고 있다면 Post를 수정한 후 SUCCESS 메시지를 반환한다.
Post(id=7)의 내용을 "안뇽하세요" 에서 "Update!"로 수정
# postings/views.py
class CommentView(View):
...
@SignInDecorator
def put(self, request, comment_id):
try:
data = json.loads(request.body)
if not Comment.objects.filter(id=comment_id).exists():
return JsonResponse({"MESSAGE":"NO_Comment"}, status=400)
if Comment.objects.get(id=comment_id).user != request.user:
return JsonResponse({"MESSAGE":"Unauthroized"}, status=403)
Comment.objects.filter(id=comment_id).update(comment = data['comment'])
return JsonResponse({"MESSAGE":"SUCCESS"}, status=201)
except KeyError:
return JsonResponse({"MESSAGE":"KEY_ERROR"}, status=400)