簡體   English   中英

DRF使用PUT方法丟失CSRF令牌

[英]DRF loses CSRF token with PUT method

我的項目中有一個常規的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')

當我發送GET或POST到payments_manager/payments/ ,它可以很好地工作。 另外,當我將GET發送到pyments_manager/payments/<int:pk>/它也很好用。

問題是當我將PUT發送到payments_manager/payments/<int:pk>/ ,因為我得到以下信息:

在此處輸入圖片說明

我不知道為什么,但是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完美地工作。

為了使用會話身份驗證並執行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版本。

在此處輸入圖片說明

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM