Session - Login Decorator

Sung Jun Jin·2020년 4월 16일
0

위코드 세션 정리

목록 보기
9/12

JWT를 이용한 사용자 토큰 검증

Authentication 절차가 끝나면 사용자는 access_token을 발급받는다.

POST /user/sign-in HTTP/1.1
Accept: application/json, */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 53
Content-Type: application/json
Host: localhost:8000
User-Agent: HTTPie/0.9.8

{
    "email": "test05@test.net",
    "password": "123123123"
}

HTTP/1.1 200 OK
Content-Length: 119
Content-Type: application/json
Date: Thu, 16 Apr 2020 12:58:56 GMT
Server: WSGIServer/0.2 CPython/3.8.2
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

{
    "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjo2fQ.OhgYyfgDfZIYKt5k9GmE9VpIYKRqqAy6X_hCC_8BIU4"
}

발급받은 토큰은 유저가 요청하는 request에 대한 권한을 검증할때 쓰인다.
ex) 회원이 댓글을 작성할때 정상적으로 로그인이 되어 있는지에 대한 여부, 댓글 작성의 권한이 있는지에 대한 여부

절차는 다음과 같다

  1. 유저는 request를 보낼때 access token을 첨부해서 보낸다
  2. 서버에서는 유저가 보낸 access token을 복호화 한다
  3. 복호화된 데이터를 통해 user_id를 얻는다
  4. user_id를 통해 DB에서 해당 저의 권한을 확인하고 권한을 가지고 있으면 요청을 처리한다
  5. 유저가 권한을 가지고 있지 않으면 401(Unauthorized Response) 혹은 다른 에러 코드를 return한다.

하지만 유저의 권한을 검증할때 매번 이와 같은 절차를 반복하는 코드를 작성할수는 없다. 코드의 가독성이 떨어지고 개발자의 실수가 충분히 일어날수 있다. decorator를 작성하면 decorator 함수가 무조건 먼저 실행되기 때문에. 자주 사용하는 권한 검증에 대한 로직을 decorator로 구현하면 편하게 가져다가 사용할 수 있다.

decorator는 자주 사용되므로 django app에 utils.py를 따로 작성하여 관리하는게 좋다

def login_required(func):
  secret = SECRET_KEY
  def wrapper(self, request, *args, **kwargs):
    try:
      payload = jwt.decode(request.headers['Authorization'], secret, algorithm = 'HS256')
      user_id = payload['user_id']
      request.user = User.objects.get(id = user_id)
      return func(self, request, *args, **kwargs)
    except jwt.DecodeError:
      return JsonResponse({'Message' : 'INVALID_TOKEN'}, status = 400)
    except User.DoesNotExit:
      return JsonResponse({'Message' : 'INVALID_USER'}, status = 400)
  return wrapper

하나하나 살펴보자

payload = jwt.decode(request.headers['Authorization'], secret, algorithm = 'HS256')

request에 header에서 가져온 access_token을 decoding한다. 정상적으로 decode되면 사용자의 id 정보가 담긴 payload({'user_id' : 1})가 담긴다. decoding이 실패할 경우를 대비해 jwt.DecodeError 예외처리를 해준다.

user_id = payload['user_id']

payload는 dictionary 형식이므로 bracket notation을 통해 사용자의 id를 가져온다.

request.user = User.objects.get(id = user_id)

기존 DB와 대조해 사용자가 존재하는지 확인한다. 사용자가 존재하지 않을 경우를 대비해 User.DoesNotExist 예외처리를 해준다. 정상적으로 사용자가 존재할 경우, request 객체에 user 변수를 만들어 인가가 완료된 사용자의 객체를 담아준다. request.user 변수는 나중에 decorator 이후 실행된 함수에서 유용하게 사용된다.

profile
주니어 개발쟈🤦‍♂️

0개의 댓글