Backend/Django

Django Middleware에서 API 접근 권한 검증하기 (Django, DRF의 request.user 와 Middleware에의 AnonymousUser)

뒷골목프로그래머 2023. 5. 2. 18:34
반응형

안녕하세요. 글쓰는 개발자입니다.

 

 어플리케이션을 개발하다 보면, 서비스 내 정의된 권한 레벨에 따라 접근 가능한 API가 상이한 경우가 많습니다. Django에서 'request.user' 를 통해 User 객체를 불러오고 권한 레벨에 해당하는 인자를 검증하여 403 Forbidden Error 를 반환 할 수 있습니다.

 

 다양한 방법이 있겠지만, Django Custom Middelware 를 활용해 End Point가 '/admin/' 으로 시작하는 API에 대해 권한 검증하는 방법을 소개 합니다.

 

 그런데 저는 이 과정에서 간단히 Middleware에서 response 객체 생성 이전에 request.user 를 활용해 User 객체를 받아오면 된다고 생각했지만, 지속적으로 'AnonymousUser' 또는 이전에 로그인한 User의 캐시된 정보를 불러오는 현상이 발생했습니다. Pycharm Debugger를 활용해 이 문제를 해결한 과정도 간단히 소개 드리겠습니다.

(실제 인증 과정은 훨씬 복잡할 수 있습니다. 본 포스트는 SimpleJWT 를 사용했습니다.)

 

 

관리자 권한 검증

목적은 관리자 권한이 필요한 API를 view에 도달하기 전에 Middleware에서 처리하는 것 입니다.  아래와 같이project_root/middlware/my_custom_middleware.py 를 만들어줍니다. 그리고 settings.py 맨 아래에 추가합니다.

 "/api/admin/" 으로 시작하는 경우 request로부터 user의 권한을 검증하는 로직이 포함되어 있습니다.

 

 

간단히 self.response 호출 전에 request.user 를 사용하지 않고, 번거롭게 _is_admin_api_authenticated 와 _get_token_user를 선언한 이유를 확인하기 위해 메소드를 하나씩 살펴보겠습니다.

 

_is_admin_api_authenticated

 Django Middleware를 사용해 보신적이 있는 분들은 의아하실 수도 있을 것 입니다. 간단히, request.user로 로그인 사용자 정보를 불러오면 될텐데 소스코드가 왜 저렇게 길어졌을까? 하는 의문이 있으실 것 입니다. 그 이유를 Pycharm Debugger를 활용해서 Line by Line으로 확인해보겠습니다. 'test2' 라는 계정으로 진행하겠습니다.

 

 

 좌측 디버거를 보시면, request.user에 뜬금없이 admin이 들어가 있는 것을 확인하실 수 있습니다. 하지만, SimpleLazyObject 객체가 _get_token_user 메소드를 인자로 반환한 결과를 request.user에 적용시키자 정상적으로 'test2'로 바뀐 것을 확인하실 수 있습니다.

 

 

_get_token_user

JWTAuthentication 직접 호출

 

 authenticator로 JWTAuthentication() 을 불러왔습니다. 저는 프로젝트에서 DjangRestFramework의 SimpleJWT를 활용했습니다. (다른 모듈을 사용하셨더라도 위와 같이 직접 호출하여 진행하시면 됩니다.)

 

settings.py DRF DEFAULT_AUTHENTICATION_CLASSES

 

 그래서 rest_framework_simplejwt.authentication import JWTAuthentication 을 통해 직접 인증을 진행하고 User 객체와 token 정보를 tuple로 반환 받아 request.user 객체에 request에 실제 담겨 온 user 정보를 주입시켰습니다. 그렇다면, 왜 이런 작업을 번거롭게 했을까요?

 

 

Django Middleware와 DRF authentication classes 실행 순서

 Custom Middleware에서 굳이 settings.py에 적용시킨 JWTAuthentication 을 직접 호출해 인증을 진행한 이유는 Django Middleware 가 모두 실행된 다음에 DRF authentication classes 가 실행되기 때문입니다. 

 

 이러한 동작 순서로 인해 settings.py에 정의된 모든 Middleware가 동작한 후 DRF Authentication classes가 동작하니 위 소스코드와 같이 get_response 호출 이전에 일반적인 방법으로 호출 할 경우, 로그인한 사용자를 정상적으로 가져올 수 없는 것 입니다.

 

 

참고

https://batiste.medium.com/django-rest-framework-authentication-and-django-middleware-why-request-user-is-anonymous-d9d62bddf4f6

 

Django Rest Framework authentication and Django Middleware: Why request.user is anonymous?

When using DRF and in in the context of Django middleware I have often found to be the case that request.user is an AnonymousUser. Why is…

batiste.medium.com

https://groups.google.com/g/django-rest-framework/c/YvWiNNCmk8o

 

AnonymousUser in DjangoRestFramework Middleware

Hi, Here is custom middleware, which works for this case, also attached project with applied changes -  it now show "admin" instead of AnonymousUser. Reason why DRF doesn't show user in middleware - DRF does not provide it's own middleware for this and

groups.google.com

 

반응형