안녕하세요. 글쓰는 개발자입니다.
어플리케이션을 개발하다 보면, 서비스 내 정의된 권한 레벨에 따라 접근 가능한 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
authenticator로 JWTAuthentication() 을 불러왔습니다. 저는 프로젝트에서 DjangRestFramework의 SimpleJWT를 활용했습니다. (다른 모듈을 사용하셨더라도 위와 같이 직접 호출하여 진행하시면 됩니다.)
그래서 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://groups.google.com/g/django-rest-framework/c/YvWiNNCmk8o
'Backend > Django' 카테고리의 다른 글
Django Swagger(drf_yasg) 수정하여 FastAPI + Pydantic 처럼 API문서 자동화 하기 (0) | 2023.05.02 |
---|---|
Django Base Model 을 통한 Model 중복 제거 (db_table, primary_key), __init_subclass__ (0) | 2023.05.02 |
[Django + Redis + Celery] Python 분산 비동기 작업 큐 (Distributed Task/Job Queue) 튜토리얼 (0) | 2023.03.31 |
Django 실전 운용 - 2. Django project 생성 (0) | 2022.12.25 |
Django 실전 운용 - 1. Project Base 구축 필요성 (요구 사항과 필요한 기술) (0) | 2022.12.23 |