如何通过 jquery ajax 与 FormData 一起发送 AntiForgeryToken (CSRF)

Posted

技术标签:

【中文标题】如何通过 jquery ajax 与 FormData 一起发送 AntiForgeryToken (CSRF)【英文标题】:How to send AntiForgeryToken (CSRF) along with FormData via jquery ajax 【发布时间】:2015-12-23 03:40:56 【问题描述】:

所以我想通过 AJAX 将 POST 文件上传和 AntiForgeryToken 一起上传。这是我的代码:

查看

@using (html.BeginForm("Upload", "RX", FormMethod.Post, new id = "frmRXUpload", enctype = "multipart/form-data"))

     @Html.AntiForgeryToken()
     @Html.TextBoxFor(m => m.RXFile, new .type = "file")
     ...rest of code here


<script>
    $(document).ready(function()
        $('#btnRXUpload').click(function () 
            var form = $('#frmRXUpload')

            if (form.valid()) 
                var formData = new FormData(form);
                formData.append('files', $('#frmRXUpload input[type="file"]')[0].files[0]);
                formData.append('__RequestVerificationToken', fnGetToken());

                $.ajax(
                    type: 'POST',
                    url: '/RX/Upload',
                    data: formData,
                    contentType: false,
                    processData: false
                )
            
        )
    )
</script>

控制器

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Upload()

    //rest of code here

我明白了

无法解密防伪令牌。如果此应用程序由 Web Farm 或集群托管

通过提琴手出错。知道如何解决这个问题吗?

我找到了答案:

<script>
        $(document).ready(function()
            $('#btnRXUpload').click(function () 
                var form = $('#frmRXUpload')

                if (form.valid()) 
                    var formData = new FormData(form.get(0)); //add .get(0)
                    formData.append('files', $('#frmRXUpload input[type="file"]')[0].files[0]);

                    $.ajax(
                        type: 'POST',
                        url: '/RX/Upload',
                        data: formData,
                        contentType: false,
                        processData: false
                    )
                
            )
        )
    </script>

【问题讨论】:

什么是fnGetToken() 如果应用程序未托管在 Web Farm 或集群上,此行为是否会改变?非 ajax 请求有效吗? @StephenMuecke:获取令牌字符串的函数 是的,但它返回了什么?为什么不按照我对您的previous questions 之一的回答使用var formdata = new FormData($('form').get(0)); 错误消息表明它可能与this question的答案之一有关 【参考方案1】:

终于找到答案了:

我只需要在表单中添加.get(0),代码如下:

<script>
        $(document).ready(function()
            $('#btnRXUpload').click(function () 
                var form = $('#frmRXUpload')

                if (form.valid()) 
                    var formData = new FormData(form.get(0)); //add .get(0)
                    formData.append('files', $('#frmRXUpload input[type="file"]')[0].files[0]);
                    //formData.append('__RequestVerificationToken', fnGetToken()); //remark this line

                    $.ajax(
                        type: 'POST',
                        url: '/RX/Upload',
                        data: formData,
                        contentType: false,
                        processData: false
                    )
                
            )
        )
    </script>

【讨论】:

【参考方案2】:

您需要将令牌添加到请求标头,而不是表单。像这样:

        if (form.valid()) 
            var formData = new FormData(form);
            formData.append('files', $('#frmRXUpload input[type="file"]')[0].files[0]);

            $.ajax(
                type: 'POST',
                url: '/RX/Upload',
                data: formData,
                contentType: 'multipart/form-data',
                processData: false,
                headers: 
                    '__RequestVerificationToken': fnGetToken()
                 
            )
        

编辑 回顾我自己是如何解决这个问题的,我记得标准的 ValidateAntiForgeryTokenAttribute 在 Request.Form 对象中查找,该对象并不总是为 AJAX 请求填充。 (在您的情况下,文件上传需要 multipart/form-data 内容类型,而 CSRF 令牌的表单发布需要 application/x-www-form-urlencoded。您设置了 contentType=false,但这两个操作需要冲突的内容类型,这可能是您的一部分问题)。因此,为了验证服务器上的令牌,您需要为您的操作方法编写一个自定义属性,以检查请求标头中的令牌:

public sealed class ValidateJsonAntiForgeryTokenAttribute
                            : FilterAttribute, IAuthorizationFilter

    public void OnAuthorization(AuthorizationContext filterContext)
    
        if (filterContext == null)
        
            throw new ArgumentNullException("filterContext");
        

        var httpContext = filterContext.HttpContext;
        var cookie = httpContext.Request.Cookies[AntiForgeryConfig.CookieName];
        AntiForgery.Validate(cookie != null ? cookie.Value : null,
                             httpContext.Request.Headers["__RequestVerificationToken"]);
    

更多信息(现在有点过时了)here。

【讨论】:

令牌可以添加到FormData。如果 OP 的当前代码不起作用,那么这也不起作用。

以上是关于如何通过 jquery ajax 与 FormData 一起发送 AntiForgeryToken (CSRF)的主要内容,如果未能解决你的问题,请参考以下文章

jquery异步ajax与服务器通信过程中如何通过then方法链式传递多层数据

jquery异步ajax与服务器通信过程中如何通过then方法链式传递多层数据 --转载

ajax文件上传

用JQuery Ajax 与一般处理程序 请求数据无刷新,以及如何调试错误

通过表单与 jquery/ajax 发送 json

AJAX/jQuery 和 MySQL?