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

Posted

技术标签:

【中文标题】如何在 Dropzone 上传请求的标头中包含 CSRF 令牌?【英文标题】:How to include the CSRF token in the headers in Dropzone upload request? 【发布时间】:2015-07-20 20:33:45 【问题描述】:

我正在开发一个单页应用程序,并且我正在使用 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 上传在标头中传递 CSRF 令牌,以免出现异常?

【问题讨论】:

您写了“Dropbox”,但我在您的问题中没有看到任何与 Dropbox 相关的内容。也许你的意思是说 Dropzone?我现在要删除 Dropbox 标签。 感谢您通知我。我真是太愚蠢了。 【参考方案1】:

哇!惊人的反馈和建议!我对每条回复都做了一些处理,使其符合我的需要。

因此,让我将我现在使用 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>

【讨论】:

【参考方案2】:

其他答案似乎都没有指出你首先需要将meta标签添加到你的布局刀片文件中,大概是因为默认刀片布局文件有它,但为了便于参考,可以添加如下:

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

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

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"
  );
);

【讨论】:

【参考方案3】:

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

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

【讨论】:

【参考方案4】:

对于那些来到这里并正在寻找 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')
    
);

【讨论】:

【参考方案5】:
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 */
            );
        );

你可以这样包含它。

【讨论】:

【参考方案6】:

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

$("#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 网站上的文档:dropzonejs.com/#config-headers 只是想稍微澄清一下命名,默认的 Laravel 安装正在寻找'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')。 laravel.com/docs/5.5/csrf#csrf-x-csrf-token 您能否编辑代码并将标头参数从X-CSRFToken 更改为X-CSRF-Token。新人可能会花费很多时间来弄清楚这一点。 对于 Yii (php) 框架,当我在 Dropzone 配置中设置为 'X-CSRF-TOKEN''X-Csrf-Token' 并在服务器端读取为 $_SERVER['HTTP_X_CSRF_TOKEN'] 时,它可以工作。但是如果我在 Dropzone 配置中设置为'X_CSRF_TOKEN',它不会显示在服务器端。只是一个注释,而不是抱怨。【参考方案7】:
you can add a headers.

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

【讨论】:

是的,这与 OP 自己发布并接受的解决方案相同【参考方案8】:

这也很好用:

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

【讨论】:

【参考方案9】:

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

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

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

【讨论】:

【参考方案10】:

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

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

【讨论】:

【参考方案11】:

我相信处理这个问题的最好方法是根据 Django 文档为所有 ajax 帖子(使用 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 帖子时出现拼写错误。

'X-CSRF-Token'

应该是

'X-CSRFToken'

【讨论】:

将其设置为默认的 ajax 帖子会产生我稍后遇到的另一个问题。我试图使用第三方服务并卡住了,因为我无法理解错误是什么。后来我意识到,默认设置是向第三方发送 CSRF 令牌,这导致了问题。 :) 好点,值得记住!会说判断电话。如果您的代码中有更多的本地 ajax 调用,那么对远程调用进行异常处理可能更有意义,或者在相反的情况下反之。无论最终产生更少的工作。干杯!【参考方案12】:

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

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

【讨论】:

根据the jQuery doc on this function,“不推荐使用它。”另外,我认为这可能只会影响通过 jQuery 库中的$.ajax() 进行的 AJAX 调用?

以上是关于如何在 Dropzone 上传请求的标头中包含 CSRF 令牌?的主要内容,如果未能解决你的问题,请参考以下文章

AWS-amplify 在请求中包含 cognito Authorization 标头

是否可以在XMLHttpRequest标头中包含antiforgerytoken?

在 python 请求中包含多个标头

如何在 PHP 的 cURL POST HTTP 请求中包含授权标头?

强制 Safari 在 jQuery GET 请求中包含 Origin 标头

WCF - 如何在服务响应中包含/添加 WS-Addressing“TO”标头