include antiforgerytoken in ajax post ASP.NET MVC

2018-12-31 20:02发布

问题:

I am having trouble with the AntiForgeryToken with ajax. I\'m using ASP.NET MVC 3. I tried the solution in jQuery Ajax calls and the Html.AntiForgeryToken(). Using that solution, the token is now being passed:

var data = { ... } // with token, key is \'__RequestVerificationToken\'

$.ajax({
        type: \"POST\",
        data: data,
        datatype: \"json\",
        traditional: true,
        contentType: \"application/json; charset=utf-8\",
        url: myURL,
        success: function (response) {
            ...
        },
        error: function (response) {
            ...
        }
    });

When I remove the [ValidateAntiForgeryToken] attribute just to see if the data (with the token) is being passed as parameters to the controller, I can see that they are being passed. But for some reason, the A required anti-forgery token was not supplied or was invalid. message still pops up when I put the attribute back.

Any ideas?

EDIT

The antiforgerytoken is being generated inside a form, but I\'m not using a submit action to submit it. Instead, I\'m just getting the token\'s value using jquery and then trying to ajax post that.

Here is the form that contains the token, and is located at the top master page:

<form id=\"__AjaxAntiForgeryForm\" action=\"#\" method=\"post\">
    @Html.AntiForgeryToken()
</form>

回答1:

You have incorrectly specified the contentType to application/json.

Here\'s an example of how this might work.

Controller:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View();
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Index(string someValue)
    {
        return Json(new { someValue = someValue });
    }
}

View:

@using (Html.BeginForm(null, null, FormMethod.Post, new { id = \"__AjaxAntiForgeryForm\" }))
{
    @Html.AntiForgeryToken()
}

<div id=\"myDiv\" data-url=\"@Url.Action(\"Index\", \"Home\")\">
    Click me to send an AJAX request to a controller action
    decorated with the [ValidateAntiForgeryToken] attribute
</div>

<script type=\"text/javascript\">
    $(\'#myDiv\').submit(function () {
        var form = $(\'#__AjaxAntiForgeryForm\');
        var token = $(\'input[name=\"__RequestVerificationToken\"]\', form).val();
        $.ajax({
            url: $(this).data(\'url\'),
            type: \'POST\',
            data: { 
                __RequestVerificationToken: token, 
                someValue: \'some value\' 
            },
            success: function (result) {
                alert(result.someValue);
            }
        });
        return false;
    });
</script>


回答2:

Another (less javascriptish) approach, that I did, goes something like this:

First, an Html helper

public static MvcHtmlString AntiForgeryTokenForAjaxPost(this HtmlHelper helper)
{
    var antiForgeryInputTag = helper.AntiForgeryToken().ToString();
    // Above gets the following: <input name=\"__RequestVerificationToken\" type=\"hidden\" value=\"PnQE7R0MIBBAzC7SqtVvwrJpGbRvPgzWHo5dSyoSaZoabRjf9pCyzjujYBU_qKDJmwIOiPRDwBV1TNVdXFVgzAvN9_l2yt9-nf4Owif0qIDz7WRAmydVPIm6_pmJAI--wvvFQO7g0VvoFArFtAR2v6Ch1wmXCZ89v0-lNOGZLZc1\" />
    var removedStart = antiForgeryInputTag.Replace(@\"<input name=\"\"__RequestVerificationToken\"\" type=\"\"hidden\"\" value=\"\"\", \"\");
    var tokenValue = removedStart.Replace(@\"\"\" />\", \"\");
    if (antiForgeryInputTag == removedStart || removedStart == tokenValue)
        throw new InvalidOperationException(\"Oops! The Html.AntiForgeryToken() method seems to return something I did not expect.\");
    return new MvcHtmlString(string.Format(@\"{0}:\"\"{1}\"\"\", \"__RequestVerificationToken\", tokenValue));
}

that will return a string

__RequestVerificationToken:\"P5g2D8vRyE3aBn7qQKfVVVAsQc853s-naENvpUAPZLipuw0pa_ffBf9cINzFgIRPwsf7Ykjt46ttJy5ox5r3mzpqvmgNYdnKc1125jphQV0NnM5nGFtcXXqoY3RpusTH_WcHPzH4S4l1PmB8Uu7ubZBftqFdxCLC5n-xT0fHcAY1\"

so we can use it like this

$(function () {
    $(\"#submit-list\").click(function () {
        $.ajax({
            url: \'@Url.Action(\"SortDataSourceLibraries\")\',
            data: { items: $(\".sortable\").sortable(\'toArray\'), @Html.AntiForgeryTokenForAjaxPost() },
            type: \'post\',
            traditional: true
        });
    });
});

And it seems to work!



回答3:

it is so simple! when you use @Html.AntiForgeryToken() in your html code it means that server has signed this page and each request that is sent to server from this particular page has a sign that is prevented to send a fake request by hackers. so for this page to be authenticated by the server you should go through two steps:

1.send a parameter named __RequestVerificationToken and to gets its value use codes below:

<script type=\"text/javascript\">
    function gettoken() {
        var token = \'@Html.AntiForgeryToken()\';
        token = $(token).val();
        return token;
   }
</script>

for example take an ajax call

$.ajax({
    type: \"POST\",
    url: \"/Account/Login\",
    data: {
        __RequestVerificationToken: gettoken(),
        uname: uname,
        pass: pass
    },
    dataType: \'json\',
    contentType: \'application/x-www-form-urlencoded; charset=utf-8\',
    success: successFu,
});

and step 2 just decorate your action method by [ValidateAntiForgeryToken]



回答4:



        function DeletePersonel(id) {

                var data = new FormData();
                data.append(\"__RequestVerificationToken\", \"@HtmlHelper.GetAntiForgeryToken()\");

                $.ajax({
                    type: \'POST\',
                    url: \'/Personel/Delete/\' + id,
                    data: data,
                    cache: false,
                    processData: false,
                    contentType: false,
                    success: function (result) {

                    }
                });

        }
    

        public static class HtmlHelper
        {
            public static string GetAntiForgeryToken()
            {
                System.Text.RegularExpressions.Match value = System.Text.RegularExpressions.Regex.Match(System.Web.Helpers.AntiForgery.GetHtml().ToString(), \"(?:value=\\\")(.*)(?:\\\")\");
                if (value.Success)
                {
                    return value.Groups[1].Value;
                }
                return \"\";
            }
        }


回答5:

I know this is an old question. But I will add my answer anyway, might help someone like me.

If you dont want to process the result from the controller\'s post action, like calling the LoggOff method of Accounts controller, you could do as the following version of @DarinDimitrov \'s answer:

@using (Html.BeginForm(\"LoggOff\", \"Accounts\", FormMethod.Post, new { id = \"__AjaxAntiForgeryForm\" }))
{
    @Html.AntiForgeryToken()
}

<!-- this could be a button -->
<a href=\"#\" id=\"ajaxSubmit\">Submit</a>

<script type=\"text/javascript\">
    $(\'#ajaxSubmit\').click(function () {

        $(\'#__AjaxAntiForgeryForm\').submit();

        return false;
    });
</script>


回答6:

In Asp.Net MVC when you use @Html.AntiForgeryToken() Razor creates a hidden input field with name __RequestVerificationToken to store tokens. If you want to write an AJAX implementation you have to fetch this token yourself and pass it as a parameter to the server so it can be validated.

Step 1: Get the token

var token = $(\'input[name=\"`__RequestVerificationToken`\"]\').val();

Step 2: Pass the token in the AJAX call

function registerStudent() {

var student = {     
    \"FirstName\": $(\'#fName\').val(),
    \"LastName\": $(\'#lName\').val(),
    \"Email\": $(\'#email\').val(),
    \"Phone\": $(\'#phone\').val(),
};

$.ajax({
    url: \'/Student/RegisterStudent\',
    type: \'POST\',
    data: { 
     __RequestVerificationToken:token,
     student: student,
        },
    dataType: \'JSON\',
    contentType:\'application/x-www-form-urlencoded; charset=utf-8\',
    success: function (response) {
        if (response.result == \"Success\") {
            alert(\'Student Registered Succesfully!\')

        }
    },
    error: function (x,h,r) {
        alert(\'Something went wrong\')
      }
})
};

Note: The content type should be \'application/x-www-form-urlencoded; charset=utf-8\'

I have uploaded the project on Github; you can download and try it.

https://github.com/lambda2016/AjaxValidateAntiForgeryToken



回答7:

In Asp.Net Core you can request the token directly, as documented:

@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery Xsrf    
@functions{
    public string GetAntiXsrfRequestToken()
    {
        return Xsrf.GetAndStoreTokens(Context).RequestToken;
    }
}

And use it in javascript:

function DoSomething(id) {
    $.post(\"/something/todo/\"+id,
               { \"__RequestVerificationToken\": \'@GetAntiXsrfRequestToken()\' });
}

You can add the recommended global filter, as documented:

services.AddMvc(options =>
{
    options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
})


回答8:

I tried a lot of workarrounds and non of them worked for me. The exception was \"The required anti-forgery form field \"__RequestVerificationToken\" .

What helped me out was to switch form .ajax to .post:

$.post(
    url,
    $(formId).serialize(),
    function (data) {
        $(formId).html(data);
    });


回答9:

Feel free to use the function below:

function AjaxPostWithAntiForgeryToken(destinationUrl, successCallback) {
var token = $(\'input[name=\"__RequestVerificationToken\"]\').val();
var headers = {};
headers[\"__RequestVerificationToken\"] = token;
$.ajax({
    type: \"POST\",
    url: destinationUrl,
    data: { __RequestVerificationToken: token }, // Your other data will go here
    dataType: \"json\",
    success: function (response) {
        successCallback(response);
    },
    error: function (xhr, status, error) {
       // handle failure
    }
});

}



回答10:

In Account controller:

    // POST: /Account/SendVerificationCodeSMS
    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public JsonResult SendVerificationCodeSMS(string PhoneNumber)
    {
        return Json(PhoneNumber);
    }

In View:

$.ajax(
{
    url: \"/Account/SendVerificationCodeSMS\",
    method: \"POST\",
    contentType: \'application/x-www-form-urlencoded; charset=utf-8\',
    dataType: \"json\",
    data: {
        PhoneNumber: $(\'[name=\"PhoneNumber\"]\').val(),
        __RequestVerificationToken: $(\'[name=\"__RequestVerificationToken\"]\').val()
    },
    success: function (data, textStatus, jqXHR) {
        if (textStatus == \"success\") {
            alert(data);
            // Do something on page
        }
        else {
            // Do something on page
        }
    },
    error: function (jqXHR, textStatus, errorThrown) {
        console.log(textStatus);
        console.log(jqXHR.status);
        console.log(jqXHR.statusText);
        console.log(jqXHR.responseText);
    }
});

It is important to set contentType to \'application/x-www-form-urlencoded; charset=utf-8\' or just omit contentTypefrom the object ...