如何在 Dropzone 上传请求中的 headers 中包含 CSRF 令牌?

How to include the CSRF token in the headers in Dropzone upload request?

我正在开发一个单页应用程序,我正在使用 Laravel 5 作为 Web 服务。

所有表单都是异步提交的,我在它们上使用 beforeSend 来附加我从元标记中获取的 CSRF 令牌,如下所示:

$.ajax({
    url: '/whatever/route',
    type: 'POST',
    dataType: 'JSON',
    data: $('form#whatever-form').serialize(),
    beforeSend: function(request) {
        return request.setRequestHeader('X-CSRF-Token', $("meta[name='token']").attr('content'));
    },
    success: function(response){
        rivets.bind($('#whateverTag'), {whateverData: response});
    },
    error: function(response){
    }
});

我的所有表单都可以正常工作,但 dropzone 上传不行。它给了我一个 TokenMismatchException 异常。这是我更新个人资料照片的 dropzone 代码:

$("#mydropzone").dropzone({
    url: "/profile/update-photo",
    addRemoveLinks : true,
    maxFilesize: 5,
    dictDefaultMessage: '<span class="text-center"><span class="font-lg visible-xs-block visible-sm-block visible-lg-block"><span class="font-lg"><i class="fa fa-caret-right text-danger"></i> Drop files <span class="font-xs">to upload</span></span><span>&nbsp&nbsp<h4 class="display-inline"> (Or Click)</h4></span>',
    dictResponseError: 'Error uploading file!'
});

我也试过把beforeSend放在这里:

$("#mydropzone").dropzone({
    url: "/profile/update-photo",
    addRemoveLinks : true,
    maxFilesize: 5,
    dictDefaultMessage: '<span class="text-center"><span class="font-lg visible-xs-block visible-sm-block visible-lg-block"><span class="font-lg"><i class="fa fa-caret-right text-danger"></i> Drop files <span class="font-xs">to upload</span></span><span>&nbsp&nbsp<h4 class="display-inline"> (Or Click)</h4></span>',
    dictResponseError: 'Error uploading file!',
    beforeSend: function(request) {
        return request.setRequestHeader('X-CSRF-Token', $("meta[name='token']").attr('content'));
    },
});

我也试过像这样在我的主文件中放置一个全局 ajaxSetup:

$.ajaxSetup({
    headers: {
        'X-CSRF-TOKEN': $('meta[name="token"]').attr('content')
    }
});

还是不行。我究竟做错了什么?如何通过 dropzone 上传在 header 中传递 CSRF 令牌,以免出现异常?

好的,这段代码现在可以正常工作了:

$("#mydropzone").dropzone({
    url: "/profile/update-photo",
    addRemoveLinks : true,
    maxFilesize: 5,
    dictDefaultMessage: '<span class="text-center"><span class="font-lg visible-xs-block visible-sm-block visible-lg-block"><span class="font-lg"><i class="fa fa-caret-right text-danger"></i> Drop files <span class="font-xs">to upload</span></span><span>&nbsp&nbsp<h4 class="display-inline"> (Or Click)</h4></span>',
    dictResponseError: 'Error uploading file!',
    headers: {
        'X-CSRF-TOKEN': $('meta[name="token"]').attr('content')
    }
});

所以基本上我需要在 Dropzone 请求的 header 中添加 X-CSRFToken。现在像魅力一样工作。

您可以使用这些代码为应用程序中的每个 jquery ajax 请求添加 csrf 令牌。

$.ajaxSetup({
    headers: {
        'X-CSRF-Token': $('meta[name="_token"]').attr('content')
    }
});

我相信处理这个问题的最好方法是根据 Django 文档

为所有 ajax posts(使用 jQuery)默认设置它]

https://docs.djangoproject.com/en/1.8/ref/csrf/#ajax

function getCookie(name) {
    var cookieValue = null;
    if (document.cookie && document.cookie != '') {
        var cookies = document.cookie.split(';');
        for (var i = 0; i < cookies.length; i++) {
            var cookie = jQuery.trim(cookies[i]);
            // Does this cookie string begin with the name we want?
            if (cookie.substring(0, name.length + 1) == (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}

var csrftoken = getCookie('csrftoken');

function csrfSafeMethod(method) {
    // these HTTP methods do not require CSRF protection
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}

function sameOrigin(url) {
    // test that a given url is a same-origin URL
    // url could be relative or scheme relative or absolute
    var host = document.location.host; // host + port
    var protocol = document.location.protocol;
    var sr_origin = '//' + host;
    var origin = protocol + sr_origin;
    // Allow absolute or scheme relative URLs to same origin
    return (url == origin || url.slice(0, origin.length + 1) == origin + '/') ||
        (url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') ||
        // or any other URL that isn't scheme relative or absolute i.e relative.
        !(/^(\/\/|http:|https:).*/.test(url));
}

$.ajaxSetup({
    beforeSend: function(xhr, settings) {
        if (!csrfSafeMethod(settings.type) && sameOrigin(settings.url)) {
            // Send the token to same-origin, relative URLs only.
            // Send the token only if the method warrants CSRF protection
            // Using the CSRFToken value acquired earlier
            xhr.setRequestHeader("X-CSRFToken", csrftoken);
        }
    }
});

在您的示例中,您在将其添加到 Dropzone.js ajax post.

时出现错字

'X-CSRF-Token'

应该是

'X-CSRFToken'

我们可以在请求头中设置CSRF token。

 xhr = open("POST",logURL,true);
      //Set CSRF token in request header for prevent CSRF attack.
 xhr.setRequestHeader(CSRFHeaderName, CSRFToken);

对于使用默认 Laravel 设置的任何人:

window.Laravel = {!! json_encode([
    'csrfToken' => csrf_token(),
]) !!};

Dropzone.options.attachments = {
    url: 'upload',
    headers: {
        'X-CSRF-TOKEN': Laravel.csrfToken
    }
}

这也很好用:

$("#mydropzone").dropzone({
  url: "/profile/update-photo",
  addRemoveLinks : true,
  maxFilesize: 5,
  dictResponseError: 'Error uploading file!',
  headers: {
    'X-CSRF-Token': $('input[name="authenticity_token"]').val()
  }
});
you can add a headers.

var myDropzone = new Dropzone("#drop_id", {
    url: "/upload/",
    headers: {'x-csrftoken': $.cookie('csrftoken')},
    method:"post",  
    ...
}
Dropzone.autoDiscover = false;
        // or disable for specific dropzone:
        // Dropzone.options.myDropzone = false;

        $(function () {
            // Now that the DOM is fully loaded, create the dropzone, and setup the
            // event listeners

            var myDropzone = new Dropzone("#my-awesome-dropzone");
            myDropzone.on("addedfile", function (file) {
                /* Maybe display some more file information on your page */
            });
            myDropzone.on("sending", function (file, xhr, formData) {
                 formData.append('csrfmiddlewaretoken', document.getElementsByName('csrfmiddlewaretoken')[0].value);
                /* Maybe display some more file information on your page */
            });
        });

你可以这样包含它。

对于那些来到这里并正在寻找 Rails 解决方案的人,请使用以下代码添加 header:

  headers: {
    'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
  },

根据文档,这同样适用于 Laravel 6.x:https://laravel.com/docs/6.x/csrf#csrf-x-csrf-token

$.ajaxSetup({
    headers: {
        'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
    }
});

Django 解决方案(感谢@Rohan):

headers: {
    'X-CSRFTOKEN': $('meta[name="token"]').context.cookie.split('=')[1]
},

None 的其他答案似乎指出您首先需要将元标记添加到您的布局 blade 文件中,大概是因为默认的 blade 布局文件有它,但为了方便参考,可以添加如下:

<meta name="csrf-token" content="{{ csrf_token() }}">

然后您可以在 Dropzone 调用的参数中引用 X-CSRF-TOKEN header:

Dropzone.autoDiscover = false;
jQuery(document).ready(function($) {
  $("div#uploader").dropzone({ 
        headers: { 
            'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
        }, 
        paramName: 'attachment', 
        url: "/upload/path"
  });
});

哇!惊人的反馈和建议!我对每一个回复都做了一点修改,使之符合我的需要。

因此,让我 pass-it-forward 使用我现在使用 Flask-WTF 和“X-CSRF-Token”Dropzone Header 用于我的 FLASK 服务器的代码。

<form>
<div class="form-horizontal">
    <div class="upload-drop-zone" id="drop-zone-licenseKey">
        <div class="dz-message">
            Drag and Drop, or Click to<br> enter your new license key
        </div>
    </div>
    <script>
        var uploadLicenseKey = new Dropzone("div#drop-zone-licenseKey",{
        init: function() 
            {
                // Do Stuff
            },
        url: "/myLicenseURL",
        paramName: "myKey",
        maxFilesize: 1, //MB,
        maxFiles: 1,
        uploadMultiple: false,
        addRemoveLinks: true,
        autoProcessQueue: false, // do not upload until save is pressed
        acceptedFiles: ".txt",
        headers: { "X-CSRF-Token" : "{{ csrf_token() }}" }
        });
    </script>
</div>