在ASP.NET MVC中的Ajax中包含antiforgerytoken


167

我在使用Ajax的AntiForgeryToken时遇到麻烦。我正在使用ASP.NET MVC3。我在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是在表单内部生成的,但是我没有使用Submit动作来提交它。相反,我只是使用jquery获取令牌的值,然后尝试用ajax发布该值。

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

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

Answers:


288

您错误地指定contentTypeapplication/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>

嗨,谢谢您的快速回复。抱歉,我没有在问题中提及它;我目前不在使用Submit操作。(令牌是一种形式,但是我没有使用提交按钮来提交)。是否可以仅将内容类型更改为其他内容?
OJRaqueño13年

13
您没有使用提交动作的事实并不会改变我的答案。您需要做的就是订阅其他事件(单击按钮,单击锚点或其他任何操作,只需读取隐藏字段的值)。至于发送AJAX请求,您可以使用我的答案中提供的示例。您不应该使用contentTypeapplication/json因为服务器希望使用将该__RequestVerificationToken参数作为POST请求有效负载的一部分application/x-www-form-urlencoded
Darin Dimitrov 2013年

这段代码$(this).data('url'),如何理解我的控制器和操作的网址。请解释。感谢

2
最初的问题是关于contentType:“ application / json”。对于常规ajax帖子,显然在表单帖子中包含__RequestVerificationToken会起作用,因为它就像常规表单帖子一样。但是,当您要发布json(因此为内容类型)时,这似乎不起作用。因此,这是错误地接受以上作为答案的情况。
约翰

我需要使用“ ModelState.IsValid”吗?我怎么能知道这是行得通的?
Moran Monovich '17

61

我所做的另一种(较少使用JavaScript的方法)是这样的:

首先,一个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
        });
    });
});

而且似乎可行!


5
+1,很好。我只是一分为@Html.AntiForgeryTokenForAjaxPost二,以便一方面获得令牌名称,另一方面获得令牌的值。否则,语法高亮将全部弄乱。它最终以这种方式结束(也从返回的结果中删除了单引号,因此它的行为类似于任何MVC帮助器,例如@Url):'@Html.AntiForgeryTokenName' : '@Html.AntiForgeryTokenValue'
Askolein 2014年

4
尼特很好。有了这个,你有一个ajax调用n cshtm文件...。
bunny1985

我对这个问题不满意,因为我认为一种更简单的方法是使用AntiForgery静态类。获取HTML并替换它而不是直接获取令牌值是一种不好的做法。ASP.NET是完全开源的:github.com/ASP-NET-MVC/aspnetwebstack/blob/…(但现在值得使用仅获取令牌的自定义扩展方法编写另一个答案)
usr-local- ΕΨΗΕΛΩΝ15年

4
仅获取令牌值的更干净的方法是使用XElement。XElement.Parse(antiForgeryInputTag).Attribute("value").Value
darrunategui

3
@transformervar antiForgeryInputTag = helper.AntiForgeryToken().ToString(); return XElement.Parse(antiForgeryInputTag).Attribute("value").Value
darrunategui

45

这是如此简单!当您@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]


谢谢,伟大的工作为json帖子...我缺少contentType :(
Snziv Gupta

9

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

或使用发布方法:

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

    $.post("/something/todo/create", form.serialize());
}

我认为,仅当您的javascript也位于cshtml文件中时,此解决方案才有效。
carlin.scott

6

在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


我如何在这里使用表格序列化学生:$('#frm-student')。serialize(),
LittleDragon

6

        函数DeletePersonel(id){

                var data = new FormData();
                data.append(“ __ RequestVerificationToken”,“ @ HtmlHelper.GetAntiForgeryToken()”);

                $ .ajax({
                    输入:“ POST”,
                    网址:“ / Personel / Delete /”和ID,
                    数据:数据,
                    快取:false,
                    processData:否,
                    contentType:false,
                    成功:功能(结果){

                    }
                });

        }
    

        公共静态类HtmlHelper
        {
            公共静态字符串GetAntiForgeryToken()
            {
                System.Text.RegularExpressions.Match值= System.Text.RegularExpressions.Regex.Match(System.Web.Helpers.AntiForgery.GetHtml()。ToString(),“(?:value = \”)(。*)(? :\“)”);
                如果(值。成功)
                {
                    返回值.Groups [1] .Value;
                }
                返回“”;
            }
        }

3

我知道这是一个老问题。但是我还是会添加我的答案,可能会对像我这样的人有所帮助。

如果您不想处理控制器的post操作的结果,例如调用controller 的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>

3

在帐户控制器中:

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


不太实用,意味着您必须编写每种表单,如果表单中包含很多元素,可能会很痛苦:(
djack109

0

我尝试了很多工作环境,但没有一个为我工作。例外是“必需的反伪造表单字段” __RequestVerificationToken”。

帮助我的是将.ajax格式切换为.post:

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

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

}


0

如果令牌是由其他控制器提供的,则令牌将不起作用。例如,如果观点得到了返回的它不会工作Accounts的控制器,但你POSTClients控制器。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.