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

41
我正在开发一个单页应用程序,使用 Laravel 5 提供 Web 服务。所有表单都是异步提交的,我使用 beforeSend 在表单上附加 CSRF 令牌,该令牌从 meta 标签中获取,如下所示:

我正在开发一个单页应用程序,使用 Laravel 5 提供 Web 服务。所有表单都是异步提交的,我使用 beforeSend 在表单上附加 CSRF 令牌,该令牌从 meta 标签中获取,如下所示:

$.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上传时将CSRF令牌传递到标头中,以避免出现异常?


你写了“Dropbox”,但我在你的问题中没有看到任何与Dropbox相关的内容。也许你想说的是Dropzone?我现在会先删除Dropbox标签。 - user94559
谢谢您提醒我。那真是太愚蠢了。 - Rohan
13个回答

69

好的,所以这段代码现在运行得很好:

$("#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 请求的头部中添加 X-CSRFToken。现在运行地很好。


Dropzone网站上的文档:http://www.dropzonejs.com/#config-headers - sameers
8
只是想澄清一下命名,Laravel的默认安装正在寻找'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')。https://laravel.com/docs/5.5/csrf#csrf-x-csrf-token - Andrew Brown
你能否编辑代码,将头部参数从 X-CSRFToken 改为 X-CSRF-Token。新手可能会花费很多时间来弄清楚这个小问题。 - Eugene
对于 Yii (PHP) 框架,当我在 Dropzone 配置中设置为 'X-CSRF-TOKEN''X-Csrf-Token' 并在服务器端读取为 $_SERVER['HTTP_X_CSRF_TOKEN'] 时,它可以正常工作。但是,如果我在 Dropzone 配置中设置为 'X_CSRF_TOKEN',则在服务器端不会显示。只是一点小提示,没有抱怨。 - davneetnarang

7
您可以使用以下代码为应用程序中的每个jquery ajax请求添加csrf令牌。
$.ajaxSetup({
    headers: {
        'X-CSRF-Token': $('meta[name="_token"]').attr('content')
    }
});

2
根据 jQuery 文档中的此函数,“不推荐使用”。另外,我认为这可能只影响通过 jQuery 库中的 $.ajax() 进行的 AJAX 调用? - sameers

3
这也相当有效:
$("#mydropzone").dropzone({
  url: "/profile/update-photo",
  addRemoveLinks : true,
  maxFilesize: 5,
  dictResponseError: 'Error uploading file!',
  headers: {
    'X-CSRF-Token': $('input[name="authenticity_token"]').val()
  }
});

3
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 */
            });
        });

最初的回答:您可以以这种方式包含它。

1

我认为处理这个问题的最佳方法是按照Django文档的建议将其默认设置为所有ajax post请求(使用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-CSRFToken',而不是'X-CSRF-Token'。

将其设置为默认的ajax帖子会创建另一个问题,这是我后来遇到的。我试图使用第三方服务,但因为无法理解错误而卡住了。后来我意识到,默认设置一直在向第三方发送CSRF令牌,这一直导致问题。 :) - Rohan
好的观点值得记住!可以说是判断性调用。如果您的代码中有更多的本地ajax调用,那么也许为远程调用做出例外会更有意义,反之亦然。最终以减少工作量为准。干杯! - jb0t

1

如果你来到这里是为了寻找Rails解决方案,请使用以下代码添加标题:

  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')
    }
});

1
对于使用默认 Laravel 设置的任何人:
window.Laravel = {!! json_encode([
    'csrfToken' => csrf_token(),
]) !!};

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

0
you can add a headers.

var myDropzone = new Dropzone("#drop_id", {
    url: "/upload/",
    headers: {'x-csrftoken': $.cookie('csrftoken')},
    method:"post",  
    ...
}

是的,这就是OP自己发布并接受的相同解决方案。 - nj2237

0

哇!惊人的反馈和建议!我从每个回复中吸取了一点,并将其适应了我所需的内容。

因此,让我通过使用Flask-WTF和“X-CSRF-Token”Dropzone头部的代码来传递它。

<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>

0
Django 解决方案(感谢 @Rohan):
headers: {
    'X-CSRFTOKEN': $('meta[name="token"]').context.cookie.split('=')[1]
},

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接