Django - автоматически добавлять аутентификатор к каждой конечной точке, если не указано иное
Итак, у меня создан пользовательский аутентификатор, и у меня более 30 конечных точек. Для всех, кроме 3 конечных точек, требуется аутентификация. Поэтому я практически добавляю @custom_authenticator
в каждую функцию или @method_decorator(custom_authenticator)
в случае классов APIView
. Есть ли способ автоматически добавить это к конечным точкам и добавить декоратор, который отключает аутентификацию для определенных функций конечной точки? Например
@donotauth
def endpoint(request)
тогда endpoint()
не будет запускать аутентификатор первым. В идеале решение должно работать с пользовательским аутентификатором ниже
пользовательский аутентификатор
def cognito_authenticator(view_func=None):
if view_func is None:
return partial(cognito_authenticator)
@wraps(view_func)
def wrapped_view(request, *args, **kwargs):
# Check the cognito token from the request.
auth = request.headers.get("Authorization", None)
if not auth:
return Response(dict(error='Authorization header expected'), status=status.HTTP_401_UNAUTHORIZED)
parts = auth.split()
if parts[0].lower() != "bearer":
return Response(dict(error='Authorization header must start with bearer'),
status=status.HTTP_401_UNAUTHORIZED)
elif len(parts) == 1:
return Response(dict(error='Token not found'), status=status.HTTP_401_UNAUTHORIZED)
elif len(parts) > 2:
return Response(dict(error='Authorization header must be Bearer token'),
status=status.HTTP_401_UNAUTHORIZED)
token = parts[1]
try:
res = decode_cognito_jwt(token)
expiration = datetime.utcfromtimestamp(res['exp'])
current_utc = datetime.utcnow()
if current_utc > expiration:
return Response(dict(error=f'current time:{current_utc} is after expiration:{expiration}',
user_msg='Please login again'), status=status.HTTP_400_BAD_REQUEST)
except Exception:
# Fail if invalid
return Response(dict(error="Invalid JWT"),
status=status.HTTP_401_UNAUTHORIZED) # Or HttpResponseForbidden()
else:
# Proceed with the view if valid
return view_func(request, *args, **kwargs)
return wrapped_view
Для добавления аутентификации разного уровня в конечной точке DRF API, вы можете использовать аутентификацию на уровне проекта в сочетании с аутентификацией на уровне представления :
# config/settings.py
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
# 'rest_framework.permissions.AllowAny', to allow all
# 'rest_framework.permissions.IsAdminUser', only admin
# 'rest_framework.permissions.IsAuthenticatedOrReadOnly', only authenticated can write
'rest_framework.permissions.IsAuthenticated',
]
}
# app_name/views.py
@permission_classes((IsAdminUser, ))
def example_view(request, format=None):
content = {
'status': 'request was permitted'
}
return Response(content)
В этом случае все конечные точки защищены разрешением IsAuthenticated
.
Но представление example_view
переопределяет глобальное разрешение и использует IsAdminUser
вместо него.
Я уже отвечал на ваш похожий вопрос здесь, в котором используется подход класса аутентификации DRF. Этот подход будет применять вашу аутентификацию ко всем представлениям API по умолчанию, и вы также можете указать, какие представления не должны использовать эту аутентификацию. Это, я думаю, решит все ваши проблемы.
Но если вы действительно хотите использовать промежуточное программное обеспечение, я бы предложил просто использовать JsonResponse
вместо Response
DRF. Это связано с тем, что Response
'.accepted_renderer
обрабатывается классом APIView
'DRF или декоратором @api_view
'DRF'
Поскольку ваше промежуточное ПО не выполняет эту обработку, вы получаете ошибку. У вас может возникнуть соблазн перенести/использовать эту обработку в ваше промежуточное ПО, но это излишне, IMO.
Таким образом, ваше промежуточное программное обеспечение может быть:
from rest_framework import status
from django.http import JsonResponse
from cheers.core.api.jwt_helpers import decode_cognito_jwt
class CognitoMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
return self.get_response(request)
def process_view(self, request, view_func, view_args, view_kwargs):
auth = request.headers.get("Authorization", None)
if not auth:
return JsonResponse(dict(error='Authorization header expected'), status=status.HTTP_401_UNAUTHORIZED)
parts = auth.split()
if parts[0].lower() != "bearer":
return JsonResponse(
dict(error='Authorization header must start with bearer'), status=status.HTTP_401_UNAUTHORIZED
)
elif len(parts) == 1:
return JsonResponse(dict(error='Token not found'), status=status.HTTP_401_UNAUTHORIZED)
elif len(parts) > 2:
return JsonResponse(
dict(error='Authorization header must be Bearer token'), status=status.HTTP_401_UNAUTHORIZED
)
token = parts[1]
try:
res = decode_cognito_jwt(token)
expiration = datetime.utcfromtimestamp(res['exp'])
current_utc = datetime.utcnow()
if current_utc > expiration:
return JsonResponse(
dict(error=f'current time:{current_utc} is after expiration:{expiration}',
user_msg='Please login again'), status=status.HTTP_400_BAD_REQUEST
)
except Exception:
# Fail if invalid
return JsonResponse(
dict(error="Invalid JWT"),
status=status.HTTP_401_UNAUTHORIZED
) # Or HttpResponseForbidden()
else:
# Proceed with the view if valid
return None
Если вы хотите указать, какие представления/урлы вы хотите, чтобы это промежуточное ПО игнорировало, некоторые вещи вы можете сделать:
- Укажите отдельную ссылку для использования этим промежуточным ПО, например, параметр, в котором перечислены url, которые это промежуточное ПО будет игнорировать .
- Добавьте какой-то флаг к вашему представлению, чтобы это промежуточное ПО проверяло, игнорировать представление или нет .