Django Rest Framework - использование аутентификации сессии и токена

Я пытаюсь заставить это работать, но не знаю, возможно ли это. Так должно быть.

Я разработал веб-приложение, используя Django + Rest-Framework + jQuery, и я хочу иметь внешнее приложение, чтобы потреблять то же самое REST API, используя JWT Tokens для аутентификации.

Мой текущий конфиг такой.

settings.py

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    ],
    'DEFAULT_RENDERER_CLASS': [
        'rest_framework.renderers.JSONRenderer',
    ]
}

SIMPLE_JWT = {
    'AUTH_HEADER_TYPES': ('Bearer',),
    }

views.py

class ListFileView(APIView):
    permission_classes = (IsAuthenticated,)

    def get(self, request, *args, **kwargs):
        user = request.user

        if user:

            obj = Foo.objects.filter(owner=user)
            serializer = FooSerializer(obj, many=True)
            data = serializer.data

            return Response(data, status=status.HTTP_200_OK)

        return Response({'detail': 'You have no access to files.'}, status=status.HTTP_400_BAD_REQUEST)

Сложность в том, что когда я использую:

permission_classes = (IsAuthenticated,)

Я могу выполнить ajax звонки из внешнего приложения (с использованием действующего JWT токен), но jQuery вызовы из приложения (с аутентифицированным пользователем) завершаются неудачно с:

{"detail":"Authentication credentials were not provided."}

И с другой стороны, если я использую autentication_classes вместо permission_classes:

authentication_classes = (SessionAuthentication, BasicAuthentication)

Я могу выполнять вызовы AJAX из веб-приложения, используя jQuery, но внешние вызовы терпят неудачу с тем же 403 ошибка.

Я попытался использовать оба, как это:

class ListFileView(APIView):
    authentication_classes = (SessionAuthentication, BasicAuthentication)
    permission_classes = (IsAuthenticated,)

    def get(self, request, *args, **kwargs):
        ...

но внешние вызовы также отклоняются.

Возможно ли иметь эти два типа Auth работать вместе в одном и том же class посмотреть, или я должен разделить на две конечные точки?

РЕДАКТИРОВАТЬ

Пример звонков из приложения с jQuery:

<script type="text/javascript">

function loadTblDocs() {
  $("#tbl-docs > tbody").empty();

  $.ajaxSetup({
      headers: { "X-CSRFToken": '{{csrf_token}}' }
    });

  $.ajax({
    type: 'GET',
    contentType: "application/json; charset=utf-8",
    url: "/api/list/",
    dataType: "json",
    success: function (data) {
                console.log(data);
                }
    });
};

</script>

И внешне через VBA код:

Set web = CreateObject("WinHttp.WinHttpRequest.5.1")
web.Open "GET", "/api/list/", False
web.SetRequestHeader "Authorization", "Bearer <JWT_TOKEN_HERE>"
web.Send

1 ответ

Решение

Я не смог точно определить, что происходит в вашем случае, потому что поведение в трех случаях, которыми вы поделились, кажется непоследовательным, но я просто объясню, как определяется аутентификация и авторизация в DRF, это может помочь вам понять проблема вышла.

Вы должны быть в состоянии использовать два класса аутентификации без проблем. С DRF аутентификация работает так:

При каждом запросе DRF просматривает предоставленные классы аутентификации в порядке их определения. Для каждого класса есть 3 случая:

  1. Если он может аутентифицировать запрос с текущим классом, DRF устанавливает request.user. С этого момента этот запрос аутентифицируется.
  2. Если учетные данные для аутентификации отсутствуют, DRF пропускает этот класс
  3. Если учетные данные аутентификации присутствуют, но недействительны, например, недействительный токен JWT в заголовке авторизации, DRF вызывает исключение и возвращает ответ 403.

Представления DRF, как правило, используют классы аутентификации, определенные в файле настроек, но если вы предоставите их в представлении, настройки будут переопределены.

Авторизация вступает в игру, когда вы добавляете allow_classes к своим представлениям. license_classes контролируют доступ к вашим представлениям, поэтому при добавлении IsAuthenticated к классам разрешений представления DRF отклоняет запрос с ответом 403, если вы пытаетесь получить доступ к этому представлению без аутентифицированного пользователя.

Таким образом, в вашем первоначальном случае ваш внутренний ajax-запрос в этом случае терпит неудачу и предполагает, что в вашем сеансе запроса нет аутентифицированных пользовательских данных. Я не знаю, что может быть причиной этого, может быть, вы не обновляете сеанс запроса в представлении входа в систему (метод входа в django должен делать это автоматически).

Во втором случае вы удаляете allow_classes из представления, поэтому представление будет обслуживать запрос независимо от того, есть ли в запросе аутентифицированный пользователь или нет. Таким образом, ожидается, что ваш внутренний ajax-запрос будет выполнен успешно, но я не знаю, почему ваш внешний запрос не выполняется в этом случае.

В вашем третьем случае, с точки зрения вашего внутреннего ajax-запроса, сценарий выглядит так же, как и в первом случае, поэтому я не знаю, почему ваш внутренний ajax-запрос выполняется в этом случае успешно, но не в первом. Здесь ожидается сбой внешнего запроса, потому что вы добавили класс разрешения IsAuthenticated в представление, но не включили JWTAuthentication в authentic_classes, поэтому ваш запрос не может быть аутентифицирован с помощью вашего токена JWT здесь.