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

Если вы хотите указать, какие представления/урлы вы хотите, чтобы это промежуточное ПО игнорировало, некоторые вещи вы можете сделать:

  1. Укажите отдельную ссылку для использования этим промежуточным ПО, например, параметр, в котором перечислены url, которые это промежуточное ПО будет игнорировать
  2. .
  3. Добавьте какой-то флаг к вашему представлению, чтобы это промежуточное ПО проверяло, игнорировать представление или нет
  4. .
Вернуться на верх