본문 바로가기

Network

[인가] access token / JWT / 데코레이터 / 토큰 만료 시간

인증(Authentication) 알아보기


인가(Authorization)

▶  인가란? 

사용자가 서버에 로그인 하면 해당 사용자가 맞는지 확인하는 과정

 

 

JWT(JSON Web Token)

▶ JWT(JSON Web Token) 

http는 stateless한 특징(과거의 통신상태를 저장하지 않음)이 있어서, headers에 토큰을 보내서 확인.

그 토큰을 생성하는 방법 중 가장 널리 이용되는 방법이 JWT 이다.

 

 

▶ JWT 구조

"eyJ0eXAiOiJKVLCJiOiJIUzI1NiJ9.eyJpZCI6MmV4cCTYzNTE1MTc0Nn0.vjvArYldV2OX7jnVBqpx_EQCr4WsY4GOXqIg"

 

1) Header : 토큰의 타입과 해시알고리즘 정보가 들어간다. 헤더의 내용은 BASE64URL 방식으로 코드화해서 JWT 첫 부분 구성

예시  { "alg" : "HS256", "typ" : "JWT" }

 

2) Payload : JWT를 통해 실제로 서버 간에 전송하고자 하는 데이터 부분. BASE64URL 방식으로 코드화해서 JWT 두번째 부분 구성

exp와 같이 만료시간을 나타내는 공개 클레임과 클라이언트와 서버간 협의하에 사용하는 비공개 클레임 두가지 요소를 조합

사용자의 개인정보는 절대 넣지 않도록 한다!

예시  { "user-id" : 1, "exp" : 1539517391 }

 

3) Signature : JWT가 원본 그대로라는 것을 확인할 때 사용. BASE64URL 인코드된 header와 payload 그리고 JWT secret을 헤더에 지정된 암호 알고리즘으로 암호화하여 전송

 

▶ PyJWT : JWT를 구현할 때 사용할 수 있는 라이브러리

 

▶ JWT 인코딩하기

class SigninView(View):
    def post(self, request):
        data = json.loads(request.body)
            ...
            user = User.objects.get(email=data["email"])
            
            access_token = jwt.encode({"id": user.id }, MY_SECRET_KEY , algorithm="HS256")
            return JsonResponse({"MESSAGE": "SUCCESS", 'token' : access_token}, status=200)
            ...
        except KeyError:
            return JsonResponse({"MESSAGE": "KEY_ERROR"}, status=400)

 

입력한 이메일 주소의 유저를 jwt인코딩하여 access_token에 담는다.

※ 유저 id는 개인정보가 아닌 단지 숫자이므로 들어가도 되지만, 이외의 개인정보는 들어가지 않도록 주의!!

 

JWT 토큰 만료 시간 설정

▶ 로그인시, token_status = True or False 로 받아서

1) 유저가 로그인 유지 체크를 활성화 하면 토큰 만료 시간을 30일로 설정

2) 활성화하지 않으면 토큰 만료 시간을 2시간으로 설정

class LoginView(View):
    def post(self, request):
        data = json.loads(request.body)

        try:
            ...

            if data["token_status"] == True:
                access_token = jwt.encode(
                    {"id": user.id, "exp": datetime.utcnow() + timedelta(days=30)}, SECRET_KEY, algorithm=ALGORITHM)
		return JsonResponse({"message": "SUCCESS", "token": access_token},status=200)
            
            access_token = jwt.encode(
                {"id": user.id, "exp": datetime.utcnow() + timedelta(hours=2)}, SECRET_KEY, algorithm=ALGORITHM)

            return JsonResponse({"message": "SUCCESS", "token": access_token}, status=200)
			
            ...
            
        except KeyError:
            return JsonResponse({"message": "KEY_ERROR"}, status=400)

 

데코레이터(decorator)

▶ 데코레이터란?

어떠한 함수를 다른 함수가 실행되기 전에 자동으로 먼저 실행될 수 있도록 해주는 문법

여러 함수에서 공통적인 기능을 필요로 하는 경우에 decorator 사용

 

▶ 인증에서 데코레이터 함수 사용하기

1) 엔드포인트를 통해 생성된 JWT access token 생성 후 프론트에 전달

2) 프론트엔드는 HTTP 요청 시 "Authorization" 헤더에 토큰을 포함시켜 전달

3) decorator 함수는 "Authorization" 헤더값을 읽어들여서 JWT access token을 읽어 해당 사용자의 로그인 여부 결정

 

▶ 데코레이터 함수

V 프론트에서 전달받은 Authorization을 access_token 에 저장

V 유저의 id에 접근해서 request.user 라는 변수에 담는다. 해당 변수를 사용. 

V jwt.ExpiredSignatureError 토큰 만료 시 에러처리

※ 인코딩일 때는 algorithm='HS256' 디코딩일 때는 algorithms='HS256'

 

def login_decorator(func):
    def wrapper(self, request, *args, **kwargs):
        try:
            access_token = request.headers.get("Authorization", None)
            payload = jwt.decode(access_token, SECRET_KEY, algorithms=ALGORITHM)
            request.user = User.objects.get(id=payload["id"])

        except jwt.exceptions.DecodeError:
            return JsonResponse({"MESSAGE": "INVALID_TOKEN"}, status=400)

        except User.DoesNotExist:
            return JsonResponse({"MESSAGE": "INVALID_USER"}, status=400)

        except jwt.ExpiredSignatureError:
            return JsonResponse({"message": "EXPIRED_TOKEN"}, status=400)

        return func(self, request, *args, **kwargs)

    return wrapper

 

 

▣ 참고자료

책 "깔끔한 파이썬 탄탄한 백엔드"

위코드 세션 '인증/인가'

블로그 'https://dongsik93.github.io/til/2020/01/11/til-authorization(2)/'