How to send AntiForgeryToken (CSRF) along with For

2019-04-29 00:08发布

问题:

So I want to POST fileUpload along with AntiForgeryToken via AJAX. Here's my code :

View

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

Controller

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Upload()
{
    //rest of code here
}

I get

The anti-forgery token could not be decrypted. If this application is hosted by a Web Farm or cluster

error via fiddler. Any idea how to solve this issue?

I FOUND THE ANSWER :

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

回答1:

FINALLY FOUND THE ANSWER :

I just need to add .get(0) in the form, here's the code :

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

You need to add the token to the request headers, not the form. Like this:

        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()
                } 
            })
        }

Edit Looking back at how I solved this problem myself, I remember that the standard ValidateAntiForgeryTokenAttribute looks in the Request.Form object which doesn't always get populated for an AJAX request. (In your case, the file upload needs a multipart/form-data content type, whereas a form post for the CSRF token needs application/x-www-form-urlencoded. You set contentType=false, but the two operations need conflicting content types, which may be part of your problem). So, in order to validate the token on the server, you will need to write a custom attribute for your action method that checks for the token in the request header:

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

More info (a bit out of date now) here.