ASP.NET Core MVC防伪

时间:2017-07-22 11:47:33

标签: asp.net asp.net-mvc asp.net-core antiforgerytoken

尝试在核心mvc项目中启用防伪,但没有运气。做了什么:

添加过滤器以自动检查每个POST请求上的防伪标记。

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

以这种方式向每个页面添加了令牌生成。

@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery Antiforgery;
@{
   var antiforgeryRequestToken = Antiforgery.GetAndStoreTokens(Context).RequestToken;
}
...
...
<script>
    var antiforgeryToken = @Json.Serialize(antiforgeryRequestToken);
</script>

最后,每个客户端ajax请求都以这种方式添加RequestVerificationToken

var options = {
        url: o.url, type: 'POST', data: o.params, headers: { 'RequestVerificationToken': antiforgeryToken } };

我可以看到每个ajax请求都有令牌,但我总是为任何POST请求获得400。如果我禁用过滤器,它可以正常工作。但是一旦我启用它,asp.net核心就会在每个POST请求上启动验证,并且它总是返回400。

有什么想法吗?

更新:

我已按照评论中的说明进行操作,现在代码如下所示。 ConfigureServices方法:

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

services.AddAntiforgery(o => o.CookieName = "XSRF-TOKEN");

这是注册的中间件:

app.Use(next => context => { 
if (context.Request.Path == "/") 
{ 
    var antiforgery = app.ApplicationServices.GetService<IAntiforgery>(); 
    var token = antiforgery.GetAndStoreTokens(context); 
    context.Response.Cookies.Append("XSRF-TOKEN", token.RequestToken, new CookieOptions {HttpOnly = false}); 
} 

return next(context); 
});

我还删除了之前发送标头的任何客户端javascript代码。但它仍然无效。

2 个答案:

答案 0 :(得分:7)

你关闭了,它比你想象的要简单得多。

首先,当您使用services.AddMvc();防伪时已添加。没有必要添加任何过滤器,因此请删除它们。

然后您想要更改防伪配置。

// Old
services.AddAntiforgery(o => o.CookieName = "XSRF-TOKEN"); 
// New
services.AddAntiforgery(o => o.HeaderName = "XSRF-TOKEN");

只有在使用ajax时才需要这样做。默认情况下,防伪系统只会考虑表单数据。

接下来,在您的观看中,您可以使用@Html.AntiForgeryToken()轻松生成防伪令牌。不需要所有额外的代码。你可以摆脱它。这将创建一个隐藏的<input />,其中包含令牌的值。如果您使用<form />,则会自动为您创建此项,如果您使用asp-actionasp-controller等标记帮助程序。如果您不使用这些标记帮助程序,则可以使用asp-antiforgery="true"

现在,您的ajax请求可以使用隐藏的<input />值。这是一个例子:

$.ajax({
    method: "POST",
    url: "/api/test",
    data: data,
    beforeSend: function (xhr) {
        xhr.setRequestHeader("XSRF-TOKEN",
            $('input:hidden[name="__RequestVerificationToken"]').val());
    },
    statusCode: {
        200: function () {
            alert("Success");
        },
        400: function () {
            alert("Client error");
        },
        500: function () {
            alert("Server error");
        }
    }
});

重要的部分是beforeSend功能。您可以在Startup.cs

设置中将请求标头设置为相同的标头名称

现在您需要做的就是将[ValidateAntiForgeryToken]添加到您想要的方法中。

最后在测试之前一定要删除你添加的中间件。

这只是一种方法。官方文档here中提供了许多其他解决方案。

答案 1 :(得分:0)

确保两个Cookie具有相同的路径: enter image description here

您可以在防伪配置选项中更改此设置。如果您使用提取来拨打电话,请确保您发送的Cookie包括凭据标头。