Django 인증 절차를 Custom 하고 싶었다.
나는 모든 View에서 인증 절차를 처리하는 AuthenticationMiddleware를 조작하거나 AUTHENTICATION_CLASSES를 재정의해서 request.user를 확인하는 절차를 처리할 수 있다. 나는 AuthenticationMiddleware 보다는 AUTHENTICATION_CLASSES 를 재정의하고자 했다. 그 이유는 AuthenticationMiddleware는 상위의 위치하는 레이어이기 때문에 이걸 섣부르게 커스텀했다가는 내가 인지 못하는 부작용이 발생할 거 같았다.
AuthenticationMiddleware
AuthenticationMiddleware 는 각 View에서 인증을 위한 절차를 검사하는 middleware이다. 여기서 만약 custom 하고 싶다면 process_header를 재정의하면 된다.
class AuthenticationMiddleware(MiddlewareMixin):
def process_request(self, request):
if not hasattr(request, "session"):
raise ImproperlyConfigured(
"The Django authentication middleware requires session "
"middleware to be installed. Edit your MIDDLEWARE setting to "
"insert "
"'django.contrib.sessions.middleware.SessionMiddleware' before "
"'django.contrib.auth.middleware.AuthenticationMiddleware'."
)
request.user = SimpleLazyObject(lambda: get_user(request))
AUTHENTICATION_CLASSES
내가 커스텀하고자 했던 인증 시스템은 Login 할 때 심어놓았던 cookie 를 백엔드 서버에서 읽어 들여 인증을 거치고자 했다. 굳이 프런트에서 인증 헤더에 심지 않고 백엔드에서 모두 처리하려고 했던 이유는 다음과 같았다.
- 어차피 프론트에서도 cookie 값을 읽어 들여 헤더에 token을 심어서 보내는데, 백엔드에서 하면 프런트 쪽에서 할 일이 줄어들 거 같다.
- 보안적인 측면에서도 브라우에서 header 를 조작해 봤자 백엔드는 cookie를 바라보고 있기 때문에 header를 조작하는 것은 의미가 없다.
이러한 이유로 백엔드에서 cookie 를 통한 로그인 기능을 구현하고자 했다.
우선 login 을 할 때 cookie에서 옵션을 통해 브라우저에서 조작할 수 없도록 했다.
response.set_cookie(
domain="example.com",
maxage=60 * 60,
secure="True",
samesite="Lax",
httponly=True,
)
그리고 JWTAuthentication 를 상속받은 다음 커스텀 했다. JWTAuthentication에서는 authenticate 메서드에서 header를 읽어 들이고 있으므로 이 부분을 고쳤다.
- 원래의 JWTAuthentication:
# JWTAuthentication
class JWTAuthentication(authentication.BaseAuthentication):
"""
An authentication plugin that authenticates requests through a JSON web
token provided in a request header.
"""
...
def authenticate(self, request):
header = self.get_header(request)
if header is None:
return None
raw_token = self.get_raw_token(header)
if raw_token is None:
return None
validated_token = self.get_validated_token(raw_token)
return self.get_user(validated_token), validated_token
- 커스텀:
class CustomJWTAuthentication(JWTAuthentication):
def get_cookie(self, request):
# 이 부분에서 cookie 를 읽어들이도록 만들었다.
def authenticate(self, request):
cookie = self.get_cookie(request)
if cookie is None:
return None
raw_token = self.get_raw_token(cookie)
if raw_token is None:
return None
validated_token = self.get_validated_token(raw_token)
return self.get_user(validated_token), validated_token
커스텀을 하면서 errorMessage 도 사내 컨벤션에 따라 넣고자 했다.
...
def get_validated_token(self, raw_token):
...
raise InvalidToken(
{
"code": self.ERROR_CODE.FAIL_TOKEN_AUTH.code, # 새롭게 커스텀한 부분
"message": self.ERROR_CODE.FAIL_TOKEN_AUTH.message, # 새롭게 커스텀한 부분
"detail": {
"info": _(
"Given token not valid for any token type",
),
"messages": messages,
},
}
)
이렇게 인증 기능을 커스텀하면서 Django 의 middleware와 인증 기능을 이해하는 좋은 기회가 되었다.
'Django' 카테고리의 다른 글
[Django] - 사용자 로그인 - JWT 적용하기 - 2. JWT 이해하기 (0) | 2023.07.03 |
---|---|
사용자 로그인 - JWT 적용하기 - 1. Session 이해하기 (0) | 2023.07.02 |
Django Channels - 채팅 기능 구현하기 - 1 (0) | 2023.05.08 |
Django Channels - Consumer 살펴보기 (0) | 2023.05.05 |
WebSocket 살펴보기 - 2 (0) | 2023.05.05 |