190

我在使用 ajax 的 AntiForgeryToken 时遇到问题。我正在使用 ASP.NET MVC 3。我在jQuery Ajax 调用和 Html.AntiForgeryToken()中尝试了解决方案。使用该解决方案,现在正在传递令牌:

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) {
            ...
        }
    });

当我删除[ValidateAntiForgeryToken]属性只是为了查看数据(带有令牌)是否作为参数传递给控制器​​时,我可以看到它们正在被传递。但是由于某种原因,A required anti-forgery token was not supplied or was invalid.当我放回属性时仍然会弹出消息。

有任何想法吗?

编辑

antiforgerytoken 是在表单中生成的,但我没有使用提交操作来提交它。相反,我只是使用 jquery 获取令牌的值,然后尝试 ajax 发布它。

这是包含令牌的表单,位于顶部母版页:

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

13 回答 13

322

您错误地指定了contentTypeto application/json

这是一个如何工作的示例。

控制器:

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

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

看法:

@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>
于 2013-01-23T06:32:12.933 回答
65

我所做的另一种(更少的javascriptish)方法是这样的:

首先,一个 Html 助手

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

这将返回一个字符串

__RequestVerificationToken:"P5g2D8vRyE3aBn7qQKfVVVAsQc853s-naENvpUAPZLipuw0pa_ffBf9cINzFgIRPwsf7Ykjt46ttJy5ox5r3mzpqvmgNYdnKc1125jphQV0NnM5nGFtcXXqoY3RpusTH_WcHPzH4S4l1PmB8Uu7ubZBftqFdxCLC5n-xT0fHcAY1"

所以我们可以像这样使用它

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

它似乎工作!

于 2013-04-17T10:23:28.587 回答
57

就是这么简单!当您@Html.AntiForgeryToken()在 html 代码中使用时,这意味着服务器已签署此页面,并且从该特定页面发送到服务器的每个请求都有一个防止黑客发送虚假请求的标志。因此,要让服务器对这个页面进行身份验证,您应该经过两个步骤:

1.发送一个名为__RequestVerificationToken并获取其值的参数,使用代码如下:

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

例如进行 ajax 调用

$.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,
});

第 2 步只需通过以下方式装饰您的操作方法[ValidateAntiForgeryToken]

于 2016-02-22T14:31:46.130 回答
12

在 Asp.Net Core 中,您可以直接请求令牌,如文档所示

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

并在 javascript 中使用它:

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

您可以添加推荐的全局过滤器,如文档所示

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

更新

上述解决方案适用于作为 .cshtml 一部分的脚本。如果不是这种情况,那么您不能直接使用它。我的解决方案是先使用隐藏字段来存储值。

我的解决方法,仍在使用GetAntiXsrfRequestToken

当没有表格时:

<input type="hidden" id="RequestVerificationToken" value="@GetAntiXsrfRequestToken()">

name属性可以省略,因为我使用该id属性。

每个表单都包含此令牌。因此,无需在隐藏字段中添加相同标记的另一个副本,您还可以通过 搜索现有字段name。请注意:文档中可以有多个表单,因此name在这种情况下不是唯一的。与应该是唯一的id属性不同。

在脚本中,按 id 查找:

function DoSomething(id) {
    $.post("/something/todo/"+id,
       { "__RequestVerificationToken": $('#RequestVerificationToken').val() });
}

另一种无需引用令牌的替代方法是使用脚本提交表单。

样品形式:

<form id="my_form" action="/something/todo/create" method="post">
</form>

令牌会作为隐藏字段自动添加到表单中:

<form id="my_form" action="/something/todo/create" method="post">
<input name="__RequestVerificationToken" type="hidden" value="Cf..." /></form>

并在脚本中提交:

function DoSomething() {
    $('#my_form').submit();
}

或者使用 post 方法:

function DoSomething() {
    var form = $('#my_form');

    $.post("/something/todo/create", form.serialize());
}
于 2018-10-04T15:44:07.270 回答
8

在 Asp.Net MVC 中,当您使用@Html.AntiForgeryToken()Razor 时,会创建一个带有名称的隐藏输入字段__RequestVerificationToken来存储令牌。如果您想编写 AJAX 实现,您必须自己获取此令牌并将其作为参数传递给服务器,以便对其进行验证。

第 1 步:获取令牌

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

第 2 步:在 AJAX 调用中传递令牌

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

注意:内容类型应为'application/x-www-form-urlencoded; charset=utf-8'

我已经在 Github 上上传了项目;您可以下载并尝试一下。

https://github.com/lambda2016/AjaxValidateAntiForgeryToken

于 2016-07-20T15:20:41.303 回答
7

        功能删除人员(ID){

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

                $.ajax({
                    类型:'POST',
                    url: '/人事/删除/' + id,
                    数据:数据,
                    缓存:假,
                    处理数据:假,
                    内容类型:假,
                    成功:函数(结果){

                    }
                });

        }
    

        公共静态类 HtmlHelper
        {
            公共静态字符串 GetAntiForgeryToken()
            {
                System.Text.RegularExpressions.Match 值 = System.Text.RegularExpressions.Regex.Match(System.Web.Helpers.AntiForgery.GetHtml().ToString(), "(?:value=\")(.*)(? :\")");
                如果(价值。成功)
                {
                    返回值.Groups[1].Value;
                }
                返回 ””;
            }
        }
于 2016-09-01T16:39:23.227 回答
4

在帐户控制器中:

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

在视图中:

$.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);
    }
});

contentType设置为或从对象中'application/x-www-form-urlencoded; charset=utf-8'省略很重要...contentType

于 2018-11-09T18:12:05.000 回答
3

我知道这是一个老问题。但无论如何我都会添加我的答案,可能会帮助像我这样的人。

如果您不想处理控制器发布操作的结果,例如调用控制器的LoggOff方法Accounts,您可以按照以下版本的@DarinDimitrov 的回答:

@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>
于 2015-06-27T21:22:40.973 回答
1

如果令牌由不同的控制器提供,则令牌将不起作用。Accounts例如,如果控制器返回视图,但您返回控制器,POST它将不起作用Clients

于 2019-08-13T09:19:35.243 回答
0

创建一个负责添加令牌的方法

var addAntiForgeryToken = function (data) {
    data.__RequestVerificationToken = $("[name='__RequestVerificationToken']").val();
    return data;
};

现在在将数据/参数传递给 Action 时使用此方法,如下所示

 var Query = $("#Query").val();
        $.ajax({
            url: '@Url.Action("GetData", "DataCheck")',
            type: "POST",
            data: addAntiForgeryToken({ Query: Query }),
            dataType: 'JSON',
            success: function (data) {
            if (data.message == "Success") {
            $('#itemtable').html(data.List);
            return false;
            }
            },
            error: function (xhr) {
            $.notify({
            message: 'Error',
            status: 'danger',
            pos: 'bottom-right'
            });
            }
            });

这里我的 Action 有一个字符串类型的参数

    [HttpPost]
    [ValidateAntiForgeryToken]
    public JsonResult GetData( string Query)
    {
于 2021-08-11T06:37:02.677 回答
0

我尝试了很多解决方法,但没有一个对我有用。例外是 "The required anti-forgery form field "__RequestVerificationToken" 。

帮助我的是将表单 .ajax 转换为 .post:

$.post(
    url,
    $(formId).serialize(),
    function (data) {
        $(formId).html(data);
    });
于 2016-07-19T15:06:19.067 回答
0

随意使用以下功能:

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

}

于 2018-09-19T16:36:41.203 回答
0
 @using (Ajax.BeginForm("SendInvitation", "Profile",
        new AjaxOptions { HttpMethod = "POST", OnSuccess = "SendInvitationFn" },
        new { @class = "form-horizontal", id = "invitation-form" }))
    {
        @Html.AntiForgeryToken()
        <span class="red" id="invitation-result">@Html.ValidationSummary()</span>

        <div class="modal-body">
            <div class="row-fluid marg-b-15">
                <label class="block">                        
                </label>
                <input type="text" id="EmailTo" name="EmailTo" placeholder="forExample@gmail.com" value="" />
            </div>
        </div>

        <div class="modal-footer right">
            <div class="row-fluid">
                <button type="submit" class="btn btn-changepass-new">send</button>
            </div>
        </div>
    }
于 2021-11-17T08:55:00.787 回答