在 ASP.NET Core 中将 ValidateAntiForgeryToken 属性与 Unobtrusive Ajax 插件一起使用

Using ValidateAntiForgeryToken attribute with Unobtrusive Ajax plugin in ASP.NET Core

我有以下表格:

<form asp-action="GetUsersAPICall" asp-controller="UsersObject" asp-antiforgery="true" data-ajax="true" data-ajax-method="get" data-ajax-mode="replace" data-ajax-update="#userSearchResult">
    Enter email or name to search for: <input type="text" id="query" name="query"/>

    <input type="submit" value="Search" />
</form>

<div id="userSearchResult"></div>

(是的,我意识到我有点混合了 Unobtrusive AJAX 语法和 ASP.NET Core tag helpers)。

我的控制器中有以下操作方法:

    [HttpPost]
    [Authorize(Roles = "PatchUser")]
    //[ValidateAntiForgeryToken]
    public async Task<IActionResult> PatchUserAPICall(UserPatchViewModel vm)
    {
        if (vm == null)
        {
            return BadRequest();
        }
        else if (!ModelState.IsValid)
        {
            return View(vm);
        }
        else
        {
            bool result = await vm.User.Update();

            if (result)
            {
                return RedirectToAction("Confirmation");
            }
            else
            {
                return StatusCode((int)HttpStatusCode.InternalServerError);
            }
        }
    }

这很好用,除非我取消注释 ValidateAntiForgeryToken 属性。

我见过为 jQuery AJAX 调用进行验证的示例,但其中许多依赖于在 header 中发送它(例如在 this Q&A 中).据推测,这将使 ValidateAntiForgeryToken 按预期工作(在代码中进行一些配置更改)。希望我没有遗漏一些明显的东西,但我搜索了很多,找不到如何在 Unobtrusive AJAX 中实际添加 header,所以我什至无法尝试类似的东西,看看它是否有效。

我知道您可以使用 User.IsInRole("RoleName") 作为 Authorize 属性的替代方法。有没有办法将 Anti-Forgery 令牌作为参数发送并以这种方式发送?或者有没有办法编辑 headers 并这样做?或者有没有我还没有想到的更好的方法?

一般来说,有什么方法可以在发送前编辑 Ajax 调用吗?

首先,您的 data-ajax-method="get" 应该是 data-ajax-method="post"。这是因为您使用 asp-antiforgery="true" 会在您的表单中添加一个隐藏的 __RequestVerificationToken。通常,此隐藏字段与表单数据的其余部分一起提交。但是,__RequestVerificationToken不能在查询字符串中提交(即GET),必须在header或body(即POST)中提交.如果你做这个改变,整个事情很可能 "just work."

但是,也可以将防伪令牌放入 ajax 请求的 header 中。 jquery unobtrustive ajax 不会开箱即用,但添加起来并不难。在 jquery.unobtrusive-ajax.js 中有一个函数, asyncRequest(element, options){..} 在末尾该函数是实际的 jquery ajax 调用:

$.ajax(options);

在此调用之前,在链接代码的第 143 行,插入以下内容:

if (method === "POST") {
    var token = $("input[name='__RequestVerificationToken'][type='hidden']").val();
    if (token) {
        options.headers = { RequestVerificationToken: token };
    }
}

如果您使用 <a> 标签进行删除或其他操作,这也很有用:ajax POST:

@Html.AntiforgeryToken() //add the hidden antiforgery token, assuming you don't have a form tag.
<a href="#" data-ajax="true" data-ajax-method="post" 
  data-ajax-url="@Url.Action("Delete", new {Model.Id})" data-ajax-confirm="Are you sure?" 
  data-ajax-success="alert('success')">Delete This</a>