【问题标题】:DRF loses CSRF token with PUT methodDRF 使用 PUT 方法丢失 CSRF 令牌
【发布时间】:2019-10-08 07:15:57
【问题描述】:

我的项目中有一个常规的ModelViewSet,它与GETPOST 请求完美配合,但它因PUT 而失败,返回此错误:

{
    "detail": "CSRF Failed: CSRF token missing or incorrect."
}

这是我的 urls.py

from django.urls            import path,re_path,include
from django.utils.text      import slugify,camel_case_to_spaces
from PaymentsManagerApp     import views, models
from rest_framework         import routers

APP_NAME = 'PaymentsManagerApp'
router = routers.DefaultRouter()

router.register(r'payments', views.PaymentViewSet)

payments_list = views.PaymentViewSet.as_view({
    'get':'list',
    'post':'create'
})

payment_detail = views.PaymentViewSet.as_view({
'get':'retrieve',
'put':'update',
'patch':'partial_update',
'delete':'destroy'

})

def urlpattern_from_route(route):
    if "regex" in route and route['regex']:
        path_method = re_path
    else:
        path_method = path
    return path_method(route['path'],route['view'].as_view(),name=route['name'] if "name" in route else None)

routes_views = list(map(urlpattern_from_route,routes))
route_services = [

payment_detail = views.PaymentViewSet.as_view({
    'get':'retrieve',
    'put':'update',
    'patch':'partial_update',
    'delete':'destroy'
})

route_services = [
    path('payments/', payments_list, name='rest_payments_list'),
    path('payments/<int:pk>/', payment_detail, name='rest_payment_detail'),
]

urlpatterns = routes_views + route_services

这是我的views.py

import os
import json
from datetime                           import datetime, timedelta
from django.shortcuts                   import render
from PaymentsManagerApp                 import urls, models, serializers
from FrontEndApp                        import urls as Fronturls
from django.shortcuts                   import render,redirect
from django.contrib.auth.mixins         import LoginRequiredMixin
from django.contrib.contenttypes.models import ContentType
from django.views.generic               import View
from django.contrib.auth.models         import Permission
from GeneralApp.utils                   import get_catalogs
from django.contrib.staticfiles         import finders
from django.utils.text                  import slugify,camel_case_to_spaces
from rest_framework                     import viewsets, permissions
from rest_framework.response            import Response
from django_filters.rest_framework      import DjangoFilterBackend
from rest_framework.response            import Response
from rest_framework.filters             import OrderingFilter, SearchFilter
from django.db.models                   import Q

class PaymentViewSet(viewsets.ModelViewSet):
        exclude_from_schema = True

        permission_classes = (permissions.IsAuthenticated,)
        queryset = models.Payment.objects.all()
        serializer_class = serializers.PaymentSerializer
        filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter,)
        search_fields = ('payment_type', 'creation_user__username', 'provider__name', 'invoice', 'payment_method_type', 'payment_document_number')
        filter_fields = ('id', 'payment_type', 'creation_user', 'provider', 'is_payment_requested', 'is_paid', 'payment_method_type')

当我向payments_manager/payments/ 发送 GET 或 POST 时,它运行良好。此外,当我向pyments_manager/payments/&lt;int:pk&gt;/发送 GET 时效果很好。

问题是当我向payments_manager/payments/&lt;int:pk&gt;/ 发送 PUT 时,因为我得到以下信息:

我不知道为什么,但 DRF 丢失了登录的用户信息(您可以看到 登录 标签,而不是用户名)。

编辑

这是我在 settings.py 中的 REST_FRAMEWORK:

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.IsAuthenticated',),
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.TokenAuthentication',
        'rest_framework.authentication.SessionAuthentication',
    ),
    'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',),
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
    'PAGE_SIZE': 20,
    'DEFAULT_METADATA_CLASS': 'rest_framework.metadata.SimpleMetadata'
}

编辑 我发现只有当我使用 DRF 默认接口 (127.0.0.1:8000/es/payments_manager/payments/1/) 直接从浏览器访问端点时,才会出现错误:

我的 PUT 请求可以通过我的 javascript ajax 完美运行。

【问题讨论】:

  • 您的问题解决了吗?如果是,那是什么?
  • @JavierBuzzi 不,我没有。我发现问题仅在于 DRF 默认接口。我的 ajax 来自 javascript 仍然可以正常工作,没有任何改变。
  • 您能否更好地描述这个问题。 "I found that the problem is only with DRF default interfase." 是什么意思?什么意思?
  • @JavierBuzzi 我在问题正文中添加了解释
  • 我在示例中添加了模板,并尝试使用浏览器将现有记录放入现有记录中,并且所有内容都按预期更新(有关详细信息,请参阅我的答案)。请提供django、drf、openapi????昂首阔步????版本。

标签: python django django-rest-framework django-rest-auth


【解决方案1】:

为了使用会话身份验证并执行POST(有点奇怪)/PUT/PATCH/DELETE/等,您必须传递一个标头。

见:https://github.com/django/django/blob/8b3f1c35dd848678225e8634d6880efeeab5e796/django/middleware/csrf.py#L306

我还为你创建了一个小测试:

https://gist.github.com/kingbuzzman/20dffbc34d22a899661ac3c065e3f747#file-django_rest_framework_session_vs_token-py-L209

  response = self.client.post('/session-login/', data={'username': 'user', 'password': 'pass'})
  self.assertEqual(302, response.status_code)
  self.assertIn('csrftoken', response.cookies)
  self.assertIn('sessionid', response.cookies)

  # Don't want to go through the trouble of having to get the CSRF from the login form
  self.client.handler.enforce_csrf_checks = True

  csrftoken = self.client.cookies.get('csrftoken').value

  # NOTE: The only reason this works it's because we're passing a header along with the request.
  response = self.client.patch('/payments/%s/' % (self.payment.id), content_type='application/json',
                               data=json.dumps({'is_paid': 'Y'}), HTTP_X_CSRFTOKEN=csrftoken)
  self.assertEqual(200, response.status_code)
  self.assertEqual('Y', response.json()['is_paid'])

  # NOTE: The reason this DOES NOT works it's because we're NOT passing a header along with the request.
  response = self.client.patch('/payments/%s/' % (self.payment.id), content_type='application/json',
                               data=json.dumps({'is_paid': 'N'}))
  self.assertEqual(403, response.status_code)

编辑。

添加一个用户,登录,导航到localhost/payments/并添加一条记录,然后转到记录localhost/payments/1/并更新它(PUT)。一切正常。请添加您的 django/drf 版本。

【讨论】:

    猜你喜欢
    • 2017-01-07
    • 2017-07-24
    • 2020-11-23
    • 1970-01-01
    • 2021-07-14
    • 2018-09-07
    • 2018-03-11
    • 1970-01-01
    • 2014-12-25
    相关资源
    最近更新 更多