sign-in-decorator
westargram mission(modeling, sign-up, sign-in, authentication, authorization)중 일단락을 짓는 sign-in decoratior 생성하기
해당 서비스를 사용하는 사용자가 회원가입을 거쳐 한 번 로그인을 하게되고, http의 stateless의 성질 때문에 정보가 기록되지 않는데, 해당 사용자에게 서버는 jwt token을 발급하여 한 번 방문했던 사용자로부터 특정 request가 발생할 때, 다시 로그인 하지 않아도 되도록 서버가 정보를 기억하고, 이때 view가 사용하는 것이 jwt sign-in decorator이다.
예를 들어, 회원가입 된 사용자가 게시물을 등록한다고 가정하였을 때, view는 다음과 같다.
class PostView(View):
@sign_in_decorator
def post(self, request):
try:
user = request.user
data = json.loads(request.body)
describe = data["describe"]
image = data["image"]
validation_url(image)
Post.objects.create(
user_id = user.id,
image = image,
describe = describe
)
return JsonResponse({'MESSAGE':'CREATED'}, status = 201)
except ValidationError as e:
return JsonResponse({"MESSAGE":e.message}, status = 400)
except KeyError:
return JsonResponse({"MESSAGE":"KEY_ERROR"}, status = 400)
-------------------------------------------------------------------------------------
#decorator
def sign_in_decorator(func):
def access_token(self, request, *args, **kwargs):
try:
access_token = request.headers.get("Authorization", None) # 1
payload = jwt.decode(access_token, SECRET_KEY, algorithms = ALGORITHM) # 2
user = User.objects.get(id = payload['id']) # 3
request.user = user # 4
return func(self, request, *args, **kwargs)
except jwt.exceptions.DecodeError:
return JsonResponse({'MESSAGE':'INVALID_TOKEN'}, status = 400)
except User.DoesNotExist:
return JsonResponse({'MESSAGE':'INVALID_USER'}, status = 401)
위 code상에서 decorator를 보면,
#1 : 먼저 frontend에서 보낸 http request내 headers의 "Authorization"key의 value인 token을 access_token이라는 변수에 할당
#2 : 위 변수에 저장된 token을 decoding해서 payload라는 변수에 할당
print(payload)
{'id': 72}
#3 : payload라는 변수에 저장된 dictionary 형태의 고객정보에서 'id'값(72)으로 User table에서 해당 user를 객체 형태로 불러와서 user라는 변수에 저장
print(user)
User object (72)
#4 : #3에서 저장한 객체를 request.user
라는 변수에 할당하는데, 이 때 token을 decoding하여 얻은 고객 정보(payload)를 request에 담아 view 함수를 호출할 때 보내어 변수로 사용한다.
func(self, request, *args, **kwargs)
이 때, "request"라는 변수 또는 인자가 중복되어 반복적으로 사용 되고 있다.
1) 이 request
들은 client가 api server로 보낸 request와 모두 동일한 request인지
2) request.user
는 client가 보낸 request
와 관련이 있는 변수인지 단지, request하고, 우리 service에 가입된 회원임이 인증된 회원이라는 것을 직관적으로 전달하고자 만든 변수인지
3) 각 request
는 또 실제로 어떠한 데이터 타입으로 변수를 보내는지 혼동이 되어 각각 print를 해보았다.
request
와 request.body
print(request)
<WSGIRequest: POST '/posting/posting'>
print(request.body) #client가 body에 담아 요청한 내용
b'{"image": "https://gaussian37.github.io/assets/img/python/django/django.png", "describe": "django"}'
request
print(request)
<WSGIRequest: POST '/posting/posting'>
user
)하고 다시 변수에 할당한 request.user
print(request.user)
User object (72)
결과적으로 decorator에서 user의 정보를 request.user에 한 번 더 할당하여 func(self, request, *args, **kwargs)에 담아 보내는데 view에서는 이렇게 전달받은 request에서
1)request.body
로 body에 담긴 client의 실제 request를 꺼내서 실행하고,
2)request.user
로 실제로 request한 user의 id를 꺼내어 명령을 실행한다.
request.user=user
로 변수에 할당하는 순간, python의 문법으로 request['user'] = User object (72)
가 되고(python의 dictionary를 만들어주는 문법) request dictionary에 'user': <User: User object (72)>
의 형태로 새로운 dictionary가 생성되어 view 함수로 전달된다.
print(request.__dict__)
{'request' : {asdfwefsdjkfhweiufhjksldfjoiwejf...} .... 'headers' :{........}, 'user' : <User: User object (72)>}
#How?:
request['user'] = User object (72) #request.user = user
{'user' : User object (72)}
왜 request.__dict__
에 body는 보이지 않는지 도통 알수가 없어서 request.__dir__()
을 출력해보면,
body는 request의 dic에서는 숨겨져서 보이지 않지만, dir에서는 확인이 가능한 것을 볼 수 있다!!! (.....왜지..... 알고싶다요.....) dir이 dic의 상위 개념이라고 봤을 때, body
는 dic에서는 제외된다는 것은 알겠다. 근데 왜? 더 중요해서? 덜 중요해서? 후...