일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- decorator
- QuerySet
- **kwargs
- docker
- 파이썬입출력
- 인증인가
- 자바스크립트
- RESTfulAPI
- 자료구조
- DP
- *args
- clone-coding
- 파이썬문법
- 알고리즘
- 인터넷 네트워크
- CSS
- 코딩테스트파이썬
- 윈도우우분투듀얼부팅
- django
- 파이썬
- 백준
- bcrypt
- promise
- 해시충돌
- Python
- 리스트컴프리헨션
- clone coding
- 파이썬리스트컴프리헨션
- JavaScript
- wecode
- Today
- Total
개발기록장
[Django] 인스타그램 클론 코딩(3) - Authorization Decorator 만들고 활용하기 본문
[이전 글]
[Django] 인스타그램 클론 코딩(2) - 로그인 기능 구현
[다음 글]
[Django] 인스타그램 클론 코딩(4) - 게시물과 댓글 C.R.U.D (+ RESTful API에서 Update에는 무슨 메소드를 써야하는가...)
서비스에는 로그인을 해야지만 이용할 수 있는 서비스가 있다.
인스타그램에서는 게시물 올리기, 댓글 쓰기 등이 있다.
그런데 이런 서비스에 request를 날릴 때마다 로그인을 했는지 검증하는 구문이 있다면, 굉장히 비효율적인 코드가 될 것이다.
그래서 이런 경우에는 decorator를 이용하여 효율적으로 코드를 작성할 수 있다.
** 인증(Authentication)에 대한 설명 보러 가기
** 인가(Authorization)에 대한 설명 보러 가기
1. Authorization Decorator 만들기
import jwt
import json
from django.http import JsonResponse
from .models import User
from my_settings import SECRET
def login_decorator(func):
def wrapper(self, request, *args, **kwargs):
# login check
if "Authorization" not in request.headers:
return JsonResponse({"message": "NEED_LOGIN"}, status=401)
try:
access_token = request.headers['Authorization']
payload = jwt.decode(access_token, SECRET['secret'], algorithms=['HS256'])
login_user = User.objects.get(id=payload['user_id'])
request.user = login_user
return func(self, request, *args, **kwargs)
except jwt.DecodeError:
return JsonResponse({'message': 'INVALID_TOKEN'}, status=401)
except User.DoesNotExist:
return JsonResponse({'message': 'INVALID_USER'}, status=401)
return wrapper
-
login_decorator라는 이름의 함수에서 유저의 권한을 확인하는 로직을 처리하도록 구현하였다.
-
Django에서는 decorator 함수들을 보통 utils.py라는 파일에 별도로 저장해서, 다른 views.py에서 필요할 때마다 이 utils.py를 import 해서 decorator로 사용한다.
나는 login_decorator는 user와 관련 있는 decorator이므로 user app 디렉토리 아래 utils.py라는 파일을 만들어서 여기에 login_decorator 함수를 저장하였다.
-
login_decorator의 형태는 일반적인 decorator 함수와 비슷하다. 이 데코레이터를 실행하고 나서 수행할 API function을 파라미터로 받고, 중첩 함수인 wrapper를 return 한다.
-
실제로 로직을 처리하는 wrapper라는 내부 함수는 데코레이터가 실행되고 나서 실행되는 API function을 고려하여 self(instance 자신), request(http request)를 파라미터로 받고 더불어 확장성을 고려하여 *args와 **kwarges까지 파라미터로 받게 구현하였다.
-
첫 번째로 http request의 header에 'Authorization'이라는 key가 없다면, 토큰이 없다는 뜻이므로 로그인이 필요하다는 에러 메시지(NEED_LOGIN)를 반환하도록 했다.
-
토큰이 있는 request라면 jwt.decode() 메소드를 이용하여 토큰 정보를 복호화하여 payload라는 변수에 저장하였다.
-
복호화하는 과정에서 이 서버가 발급한 토큰이 아니라면 jwt.DecodeError가 발생하므로 try-except로 에러 처리를 해준다.
-
로그인 시 발급했던 jwt에서 payload에 user_id를 저장했다 (로그인 코드 참고). 따라서 payload 변수에서 user_id에 접근하여 login_user라는 변수에 user_id를 저장하였다.
-
이때 토큰에서 전달받은 user_id가 DB에 없는 user id 라면 User.DoesNotExist 에러가 발생하므로 try-except로 그에 따른 에러 처리도 해준다.
-
그리고 request.user에 이 login_user 즉, user의 id를 저장했다.
참고)
- Authorization이라는 키워드는 어디서 나온 걸까?
: request headers에 담긴 정보도 request body처럼 딕셔너리 형태로(정확히는 JSON타입) 받아오는데,
토큰에 대한 정보는 통상적으로 request headers에 Authorization이라는 키로 정보를 넘겨준다.
꼭 Authorization으로 할 필요는 없지만 통상적으로 쓰이기 때문에 기본 값도 Authorization이다.
- request.user는 무엇일까?
: request는 가변 객체이다. 가변 객체이기 때문에 다른 객체(또는 변수) 할당이 가능하다. 그래서 request에 user라는 객체를 새로 할당하여 여기에 user 정보를 넣어서 request로 정보를 return 하는 것이다.
- 왜 이렇게 할까?
: 데코레이터가 실행되고 나서 실행되는 API function가 인자를 어떻게 받을지 모르기 때문에 어차피 인자로 받아야 하는 request에 user정보를 담아서 주는 것이다.
이렇게 하지 않는다면, user라는 인자를 전달하기 위해 이 데코레이터 다음에 실행될 모든 API function에 추가 인자를 받을 수 있도록 구현해야 한다. 하지만 이렇게 되면 데코레이터를 쓰는 의미가 퇴색된다.
2. Authorization Decorator 활용
로그인을 해야만 사용 가능한 서비스를 처리할 때 그 서비스를 처리하는 함수 위에 login_decorator를 붙인다.
예시) 인스타그램에 게시글을 등록하는 메소드에는 반드시 로그인이 필요하므로 이 함수 위에 @login_decorator를 붙인다.
import json
from django.views import View
from django.http import JsonResponse
from .models import Post
from user.utils import login_decorator
class PostView(View):
@login_decorator
def post(self, request):
data = json.loads(request.body)
user = request.user
image_url = data.get('image_url', None)
content = data.get('content', None)
# KEY_ERROR check
if not image_url:
return JsonResponse({'message': 'KEY_ERROR'}, status=400)
post = Post.objects.create(
user = user,
image_url = image_url,
content = content
)
return JsonResponse({'message': 'SUCCESS'}, status=200)
3. http request에 토큰 보내기
위에 예시로 적은 PostView의 post메소드를 테스트하려고 보니 막상 http request에 토큰을 어떻게 보내지..? 싶었다.
구글링 해보고 몇 가지 테스트를 해본 결과, JWT 토큰은 아래와 같이 보내면 된다는 것을 알게 되었다.
http -v POST http://127.0.0.1:8000/url입력 "Authorization: 토큰입력" image_url='https://test.jpg' content="test"
request body 부분인 image_url나 content 처럼 작성하면 Authorization도 request body로 인식을 해버린다.
반드시 따옴표 부분 안에 Authorization이라는 키워드를 넣고 ':' 뒤에 토큰을 입력해서 함께 전달해주면 된다.
'TIL > Django' 카테고리의 다른 글
[Django] 인스타그램 클론 코딩(5) - 좋아요(Like) 기능 구현 (0) | 2021.02.09 |
---|---|
[Django] 인스타그램 클론 코딩(4) - 게시물과 댓글 C.R.U.D (RESTful API) (0) | 2021.02.09 |
[Django] 인스타그램 클론 코딩(2) - 로그인 기능 구현 (0) | 2021.02.02 |
[Django] 인스타그램 클론 코딩(1) - 회원가입 기능 구현 (0) | 2021.02.02 |
[Django] ManyToManyField 설정과 데이터 조회 방법 (0) | 2021.01.27 |