AllowAny разрешения, необходимые для запросов DELETE в DRF?

Я ищу здесь пояснения.

Я создал приложение для реагирования, которое подключается к Django Rest Framework (DRF). Приложение полностью конфиденциально, что означает, что никто, не прошедший аутентификацию, ничего не может делать. Для этого я использовал TokenAuthentication (пока, потому что я думаю, что SessionAuthentication будет более безопасным).

Я изо всех сил пытаюсь понять это:

  • Я могу пройти аутентификацию и установить соответствующий заголовок (авторизация) с помощью моего токена.
  • Я могу без проблем выполнять запросы GET / POST / PATCH
  • однако: запросы DELETE не работают: я получаю ошибку 401 - Несанкционированный доступ. Но мой заголовок с моим токеном в порядке. Я использую запросы DELETE для удаления объектов из моей базы данных.

Я нашел способ заставить его работать: украсив мой класс представления с помощью @permission_classes((AllowAny, )), он просто отлично работает. Но меня это не устраивает.

Итак, почему это?

В моем файле настроек есть:

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.TokenAuthentication',
    ),
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated', )
}

В документации DRF (ссылка) говорится:

DELETE requests require the user to have the delete permission on the model.

Но мой пользователь является администратором, так зачем мне явно добавлять этот декоратор?

Что я делаю неправильно? Это не лучший способ сделать это. И почему он хорошо работает без декоратора, если я делаю запрос с помощью внешнего клиента API (например, Postman - я использую Paw) ??

Для справки это мой упрощенный класс представления:

@permission_classes((AllowAny, ))
class ObservationAPIView(APIView):

    def delete(self, request, test_id, observation_id, format=None):

        # see if my object exists
        try:
            obs_object_to_delete = Observation.objects.get(pk=observation_id)
            test_obj = Test.objects.get(pk=test_id)

        except ObjectDoesNotExist:
            errormsg = {
                'observation id': observation_id,
                'test id': test_id,
                'message': 'Cannot delete this object. Observation ID or test ID not found.'
            }
            return Response(errormsg, status=status.HTTP_404_NOT_FOUND)

        # it exists, delete it 
        obs_object_to_delete.delete()

        # return the test data (not the deleted object)
        serializer = TestSerializer(test_obj, many=False)
        return Response(serializer.data)

    def patch(self, request, test_id, observation_id, format=None):

        # there is also a PATCH function to allow for edits. 
        # This does work well without the decorator.

Здесь все выглядит нормально. Нет никакого волшебства - на мой взгляд, у вас есть ошибка в приложении реакции - что не передает заголовок авторизации при запросе на удаление. Что касается AllowAny - это плохое решение. По сути, это позволяет любому пользователю (тоже не авторизованному) выполнять действие удаления. Как я уже сказал - внимательно посмотрите на свой клиентский код.

opalczynski 31.10.2018 21:24

@opalczynski Глядя на мои инструменты разработчика, похоже, что заголовок отправлен, но когда я проверяю запрос на стороне DRF, заголовка больше нет! Но только в методах DELETE. В методах POST или GET DRF видит, что мой пользователь аутентифицирован.

Wim Verhavert 02.11.2018 11:45

Супер странно :( Вы подключаетесь к бэкэнду напрямую? Или через какой-нибудь балансировщик нагрузки (например, nginx?) - может быть, есть какие-то правила, которые отключают заголовок Auth при действии Удалить?

opalczynski 02.11.2018 13:10

не в моей среде разработки. Я использую сервер разработки по умолчанию (запущенный с npm start) для своего интерфейса. И серверная часть работает в контейнере докеров, который запускает сервер с помощью этой команды: python3 manage.py runserver 0.0.0.0:8000

Wim Verhavert 02.11.2018 15:23

Снова глядя на журналы DRF, кажется, что мое внешнее приложение сначала отправляет вызов OPTIONS (я полагаю, часть предварительной проверки), мой сервер отвечает кодом 200 (так что я полагаю, все работает нормально), а затем никаких других вызовов не следует.

Wim Verhavert 02.11.2018 15:49
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
5
2 257
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Почему это происходит?
Поскольку вы добавляете DEFAULT_PERMISSION_CLASSES глобально, (в settings) DRF будет использовать этот во всех представлениях на основе классов.

. Если вы хотите использовать дополнительные / настраиваемые классы разрешений для определенного представления, вы можете добавить его внутри класса представления с помощью атрибута permission_classes как,

class SampleView(APIView):
    permission_classes = (MyCustomPermissionClass,)
    ....

В вашем случае вам нужно override поведение по умолчанию для конкретного метода.

Итак, как переопределить классы разрешений по умолчанию для частного метода способом DRF?
APIView класс метод под названием get_permissions(), который обрабатывает это

class SampleView(APIView):
    def get_permissions(self):
        if self.request.method == 'DELETE':
            return [permission() for permission in (AllowAny,)]
        return super(SampleView, self).get_permissions()
    # your code

Спасибо за ответ, но мне не нужно знать, как добавить уровень разрешений для метода удаления. Я хочу понять, почему мой уровень разрешений по умолчанию (isAuthenticated) не работает для методов DELETE.

Wim Verhavert 02.11.2018 11:48
Ответ принят как подходящий

Нашел! В URL-адресе не было завершающей косой черты, указывающей на метод удаления.

Но вместо того, чтобы выдать ошибку 404 или что-то в этом роде, DRF ответил ошибкой 401 (Несанкционированный доступ). Это заставило меня искать ошибки в авторизации.

Но все еще озадачен этим: установив декоратор @permission_classes ((AllowAny,)), конечная косая черта, похоже, не нужна для продолжения моего интерфейса после предварительной проверки. Без декоратора (и, следовательно, с соответствующими разрешениями) React не хочет продолжать работу после предварительной проверки. Если вы добавите косую черту в конце URL-адреса, все будет нормально. и так, что здесь происходит? Обходит ли allowAny CORS? И требует ли CORS завершающих слэшей?

Если кто-нибудь сможет объяснить мне такое поведение, я был бы очень благодарен.

Спасибо, что посмотрели всех !!

У меня были похожие симптомы по другим причинам,
Может быть, история отладки будет кому-то полезна.

GET, POST, PUT - работает нормально, DELETE - не работает
если всем не дано разрешение «AllowAny».

при отладке запросов в views.py обнаружено, что
в последнем случае request.user == AnonymousUser и request.auth == none.

Причина - неправильный синтаксис запроса DELETE на стороне внешнего интерфейса
(в JS fetch(url, {method: "DELETE", headers, body})
неправильное расположение фигурных скобок для опций),
что привело к потере жатки и тела, вкл. Заголовок авторизации.

Другие вопросы по теме