如何在 ASP.NET Core 3.1 中实现自定义 ValidateAntiforgeryTokenAuthorizationFilter
How to implement custom ValidateAntiforgeryTokenAuthorizationFilter in ASP.NET Core 3.1
我想实现一个过滤器,在使用 auth 令牌身份验证 (Bearer
) 时跳过防伪令牌的验证。
在 ASP.NET Core 2.2 中,ValidateAntiforgeryTokenAuthorizationFilter
and AutoValidateAntiforgeryTokenAuthorizationFilter
是 public(即使生活在 Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
命名空间中),所以我只能从后者继承并轻松覆盖 ShouldValidate
方法。
在 ASP.NET Core 3.0 中,它们成为内部的,因此不可能仅从它们继承。我可以复制粘贴代码,但这显然不是理想的解决方案。
我关注了来自 MSDN 的 Prevent Cross-Site Request Forgery (XSRF/CSRF) attacks in ASP.NET Core 文章,但它并没有真正提到与我的场景相关的任何内容。
通常情况下,如果您可以在 compile-time 确定应忽略 csrf 令牌,则可以使用 [IgnoreAntiforgeryToken]
属性。如果你想在 run-time 获得这样的能力,你可以创建一个自定义 FilterProvider
,它将 提供一个 IAntiforgeryPolicy
如果有 Authroization: Bearer json-web-token
header。
例如,我们可以创建一个自定义 AutoSkipAntiforgeryFilterProvider
如下:
public class AutoSkipAntiforgeryFilterProvider: IFilterProvider
{
private const string BEARER_STRING = "Bearer";
public int Order => 999;
public void OnProvidersExecuted(FilterProviderContext context) { }
public void OnProvidersExecuting(FilterProviderContext context)
{
if (context == null) { throw new ArgumentNullException(nameof(context)); }
if (context.ActionContext.ActionDescriptor.FilterDescriptors != null)
{
var headers = context.ActionContext.HttpContext.Request.Headers;
if (headers.ContainsKey("Authorization"))
{
var header = headers["Authorization"].FirstOrDefault();
if(header.StartsWith(BEARER_STRING,StringComparison.OrdinalIgnoreCase))
{
var FilterDescriptor = new FilterDescriptor(SkipAntiforgeryPolicy.Instance, FilterScope.Last);
var filterItem = new FilterItem( FilterDescriptor,SkipAntiforgeryPolicy.Instance);
context.Results.Add(filterItem);
}
}
}
}
// a dummy IAntiforgeryPolicy
class SkipAntiforgeryPolicy : IAntiforgeryPolicy, IAsyncAuthorizationFilter
{
// a singleton
public static SkipAntiforgeryPolicy Instance = new SkipAntiforgeryPolicy();
public Task OnAuthorizationAsync(AuthorizationFilterContext context) => Task.CompletedTask;
}
}
并在 Startup 中注册此过滤器提供程序:
services.TryAddEnumerable( ServiceDescriptor.Singleton<IFilterProvider, AutoSkipAntiforgeryFilterProvider>());
现在即使有 [ValidateAntiForgeryToken]
属性,它也会绕过 AntiForgery
。
[演示]
假设我们有一个注释为 [ValidateAntiForgeryToken]
:
的操作方法
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,Name")] XModel xModel)
{
....
}
一般情况下,它会用CSRF token保护这个方法。但是如果你发送这样的请求:
POST /XModels/Create HTTP/1.1
Authorization: Bearer Xyz
Content-Type: application/x-www-form-urlencoded
...
它不会验证 csrf 令牌。
我想实现一个过滤器,在使用 auth 令牌身份验证 (Bearer
) 时跳过防伪令牌的验证。
在 ASP.NET Core 2.2 中,ValidateAntiforgeryTokenAuthorizationFilter
and AutoValidateAntiforgeryTokenAuthorizationFilter
是 public(即使生活在 Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
命名空间中),所以我只能从后者继承并轻松覆盖 ShouldValidate
方法。
在 ASP.NET Core 3.0 中,它们成为内部的,因此不可能仅从它们继承。我可以复制粘贴代码,但这显然不是理想的解决方案。
我关注了来自 MSDN 的 Prevent Cross-Site Request Forgery (XSRF/CSRF) attacks in ASP.NET Core 文章,但它并没有真正提到与我的场景相关的任何内容。
通常情况下,如果您可以在 compile-time 确定应忽略 csrf 令牌,则可以使用 [IgnoreAntiforgeryToken]
属性。如果你想在 run-time 获得这样的能力,你可以创建一个自定义 FilterProvider
,它将 提供一个 IAntiforgeryPolicy
如果有 Authroization: Bearer json-web-token
header。
例如,我们可以创建一个自定义 AutoSkipAntiforgeryFilterProvider
如下:
public class AutoSkipAntiforgeryFilterProvider: IFilterProvider
{
private const string BEARER_STRING = "Bearer";
public int Order => 999;
public void OnProvidersExecuted(FilterProviderContext context) { }
public void OnProvidersExecuting(FilterProviderContext context)
{
if (context == null) { throw new ArgumentNullException(nameof(context)); }
if (context.ActionContext.ActionDescriptor.FilterDescriptors != null)
{
var headers = context.ActionContext.HttpContext.Request.Headers;
if (headers.ContainsKey("Authorization"))
{
var header = headers["Authorization"].FirstOrDefault();
if(header.StartsWith(BEARER_STRING,StringComparison.OrdinalIgnoreCase))
{
var FilterDescriptor = new FilterDescriptor(SkipAntiforgeryPolicy.Instance, FilterScope.Last);
var filterItem = new FilterItem( FilterDescriptor,SkipAntiforgeryPolicy.Instance);
context.Results.Add(filterItem);
}
}
}
}
// a dummy IAntiforgeryPolicy
class SkipAntiforgeryPolicy : IAntiforgeryPolicy, IAsyncAuthorizationFilter
{
// a singleton
public static SkipAntiforgeryPolicy Instance = new SkipAntiforgeryPolicy();
public Task OnAuthorizationAsync(AuthorizationFilterContext context) => Task.CompletedTask;
}
}
并在 Startup 中注册此过滤器提供程序:
services.TryAddEnumerable( ServiceDescriptor.Singleton<IFilterProvider, AutoSkipAntiforgeryFilterProvider>());
现在即使有 [ValidateAntiForgeryToken]
属性,它也会绕过 AntiForgery
。
[演示]
假设我们有一个注释为 [ValidateAntiForgeryToken]
:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,Name")] XModel xModel)
{
....
}
一般情况下,它会用CSRF token保护这个方法。但是如果你发送这样的请求:
POST /XModels/Create HTTP/1.1 Authorization: Bearer Xyz Content-Type: application/x-www-form-urlencoded ...
它不会验证 csrf 令牌。