【问题标题】:Getting CSRF token missing or incorrect for one route but not the other一条路线的 CSRF 令牌丢失或不正确,而另一条路线则没有
【发布时间】:2018-05-24 07:34:54
【问题描述】:

在 React/Django 上工作并遇到了我无法解决的问题。

在前端,有如下JS向Django API发送数据。在这种情况下,rejectedDocuemts()filenames 的数组发送到后端,因此最终可以创建一封电子邮件并将其发送给管理员进行审查。与已批准的扩展名列表不匹配的文件会出现在此列表中。这就是发生错误的地方。

submitDocuments 是提交符合批准的文件扩展名列表的文件的位置。整个files 对象被发送到服务器进行保存。这个很好用。

export function rejectedDocuments(filenames, id) {
    return function (dispatch) {
        axios.post(
            `${URL}/api/${(id)}/documents/upload/error`,
            filenames,
            { 
                headers: {
                    'content-type': 'text/plain',
                    'Authorization': 'JWT ' + sessionStorage.getItem('token')
                }
            }  
        )
    }
}

// Creates the error state to be used in components
export function submitDocuments(files, id) {
    return function (dispatch) {
        const uploaders = _.map(files, f => {
            const formData = new FormData();

            formData.append('file', f);
            return axios.post(
                `${URL}/api/${(id)}/documents/upload/success`,
                formData,
                { 
                    headers: { 
                        'content-type': 'multipart/form-data',
                        'Authorization': 'JWT ' +  sessionStorage.getItem('token')
                    }
                }          
            )
        });

        axios.all(uploaders)
        .then(response => {
            dispatch({ 
                type: SUBMIT,
                payload: response[0].status
            });
        })     
    }
}

rejectedDocuments 类是发生错误 (Forbidden (CSRF token missing or incorrect.): /api/84/documents/upload/error) 的地方。检查它们都包括Cookie: csrftoken=xyz 的网络标头。它们都有相同的 CSRF 令牌。

所以不确定发生了什么或如何解决它。 submitDocuments 工作得非常好,除了一个发送文件对象 (submitDocuments) 和另一个发送字符串数组 (rejectedDocuments) 之外,它们之间并没有什么不同。

无论如何,这里是 Django 后端:

# documents/urls.py
from django.conf.urls import url

from .views import (
    GetDocumentsAPIView, 
    RejectedDocumentsView,
    SaveDocumentAPIView
)

app_name = 'Documents'

urlpatterns = [
    url(r'^documents/upload/success', SaveDocumentAPIView.as_view(), name='upload'),
    url(r'^documents/upload/error', RejectedDocumentsView.as_view(), name='rejected'),
    url(r'^documents/', GetDocumentsAPIView.as_view(), name='documents')
]

# documents/views.py
import datetime

from django.contrib.auth import get_user_model
from django.db.models import Max
from django.views import View

from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.status import HTTP_200_OK, HTTP_400_BAD_REQUEST
from rest_framework.views import APIView

from .serializers import GetDocumentsSerializer, SaveDocumentSerializer
from .models import Uploads

User = get_user_model()

# Create your views here.
class GetDocumentsAPIView(APIView):
    permission_classes = [IsAuthenticated]

    def get(self, request, *args, **kwargs):
        data = {'id': request.user.id}
        if kwargs:
            data['sur_id'] = kwargs.get('sur_id')
        serializer = GetDocumentsSerializer(data=data)
        if serializer.is_valid(raise_exception=True):
            return Response(serializer.data, status=HTTP_200_OK)
        return Response(serializer.errors, status=HTTP_400_BAD_REQUEST)

class RejectedDocumentsView(View):
    def post(request):
        print(request)

class SaveDocumentAPIView(APIView):
    permission_classes = [IsAuthenticated]

    def post(self, request, *args, **kwargs):
        max_id = Uploads.objects.all().aggregate(Max('id'))
        if max_id['id__max'] == None:
            max_id = 1
        else:    
            max_id = max_id['id__max'] + 1
        data = {
            'user_id': request.user.id,
            'sur_id': kwargs.get('sur_id'),
            'co': User.objects.get(id=request.user.id).co,
            'date_uploaded': datetime.datetime.now(),
            'size': request.FILES['file'].size
        }
        filename = str(data['co']) + '_' + str(data['sur_id']) + '_' + str(max_id) + '_' + request.FILES['file'].name
        data['doc_path'] = filename
        self.save_file(request.FILES['file'], filename)
        serializer = SaveDocumentSerializer(data=data)
        if serializer.is_valid(raise_exception=True):
            serializer.save()
            return Response(serializer.data, status=HTTP_200_OK)
        return Response(serializer.errors, status=HTTP_400_BAD_REQUEST)

    # Handling the document
    def save_file(self, file, filename):
        with open('flu/' + filename, 'wb+') as destination:
            for chunk in file.chunks():
                destination.write(chunk)

不发布序列化,因为RejectedDocumentsView(View) 不需要序列化,而且错误似乎在出现之前就发生了。

【问题讨论】:

    标签: django reactjs csrf


    【解决方案1】:

    我重新评估了我的方法,发现它存在缺陷。数据都是通过相同的<form> 提交的,所以我想到它应该都通过相同的FormData() 提交。

    我摆脱了rejectedFiles 函数并最终将submitDocuments 函数重写为:

    export function submitDocuments(accepetedFiles, rejectedFiles, id) {
        return function (dispatch) {
            var formData = new FormData();
    
            // handle the accepetedfile and append to formData
            _.map(accepetedFiles, f => {
                formData.append('file', f);
            });
            // handle the rejectedfiles and append to formData
            _.map(rejectedFiles, r => {
                formData.append('rejected', r)
            })
    
            // Send the formDat to the server
            axios.post(
                `${URL}/api/${(id)}/documents/upload`,
                formData,
                {
                    headers: {
                        'content-type': 'multipart/form-data',
                        'Authorization': 'JWT ' + sessionStorage.getItem('token')
                    }
                }
            )
            // handling of the server response, should enable the modal to render
            .then(response => {
                dispatch({ 
                    type: SUBMIT,
                    payload: response.status
                });
            })  
        }
    }
    

    这将创建 FormData(),其中包含所有文件和包含不允许扩展名的文件名数组。

    在后端,我在urls.py 中删除了以下内容:

    url(r'^documents/upload/error', RejectedDocumentsView.as_view(), name='rejected'),
    

    最后是这样的:

    from django.conf.urls import url
    
    from . import views
    
    from .views import (
        GetDocumentsAPIView, 
        SaveDocumentAPIView
    )
    
    app_name = 'Documents'
    
    urlpatterns = [
        url(r'^documents/upload', SaveDocumentAPIView.as_view(), name='upload'),
        url(r'^documents/', GetDocumentsAPIView.as_view(), name='documents')
    ]
    

    最后,我更新了views.py 来处理request.POST 传入的数组:

    class SaveDocumentAPIView(APIView):
        permission_classes = [IsAuthenticated]
    
        def post(self, request, *args, **kwargs):
    
            # this for handlign the files we DON't want
            # files are not actually just sent to the server
            # just the files names so an email can be sent to the admin
            # to review the file types
            user_obj = User.objects.filter(id=request.user.id).first()
    
            if len(request.POST.getlist('rejected')) >= 1:
                send_mail(
                    'Invalid File Extension',
                    'The user %s %s with email %s has submitted the following files with invalid extensions: \n\n %s' % \
                    (user_obj.first_name, user_obj.last_name, user_obj.email, request.POST.getlist('rejected')),
                    'The Company <no-reply@company.com>',
                    ['admin@company.com']
                )
    
            # this is for handling the files we do want
            # it writes the files to disk and writes them to the database
            for f in request.FILES.getlist('file'):
                max_id = Uploads.objects.all().aggregate(Max('id'))
                if max_id['id__max'] == None:
                    max_id = 1
                else:    
                    max_id = max_id['id__max'] + 1
                data = {
                    'user_id': request.user.id,
                    'sur_id': kwargs.get('sur_id'),
                    'co': User.objects.get(id=request.user.id).co,
                    'date_uploaded': datetime.datetime.now(),
                    'size': f.size
                }
                filename = str(data['co']) + '_' + \
                        str(data['sur_id']) + '_' + \
                        str(max_id) + '_' + \
                        f.name
                data['doc_path'] = filename
                self.save_file(f, filename)
                serializer = SaveDocumentSerializer(data=data)
                if serializer.is_valid(raise_exception=True):
                    serializer.save()
            return Response(status=HTTP_200_OK)
    
        # Handling the document
        def save_file(self, file, filename):
            with open('fileupload/' + filename, 'wb+') as destination:
                for chunk in file.chunks():
                    destination.write(chunk)
    

    虽然马虎,但它做了我需要它做的事情。

    【讨论】:

      猜你喜欢
      • 2021-08-20
      • 2013-01-26
      • 2016-04-12
      • 2021-07-14
      • 2013-07-30
      • 2011-12-26
      • 2014-03-29
      • 1970-01-01
      • 2014-12-25
      相关资源
      最近更新 更多