我一直在尝试重新创建一个基于Ajax的ValidateAntiForgeryToken版本 - 有很多博客文章介绍如何为以前的MVC版本实现这一点,但是对于最新的MVC 6,没有任何代码是相关的。然而,我追求的核心原则是让验证程序查看Cookie和Header中的__RequestVerificationToken
,而不是将Cookie与表单值进行比较。我正在使用MVC 6.0.0-rc1-final、dnx451框架,并且所有Microsoft.Extensions库都是1.0.0-rc1-final。
我的初始想法是继承ValidateAntiForgeryTokenAttribute,但是查看源代码后,我需要返回自己实现的授权过滤器来让它查看Header。
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class ValidateAjaxAntiForgeryTokenAttribute : Attribute, IFilterFactory, IFilterMetadata, IOrderedFilter
{
public int Order { get; set; }
public bool IsReusable => true;
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
{
return serviceProvider.GetRequiredService<ValidateAjaxAntiforgeryTokenAuthorizationFilter>();
}
}
因此,我制作了自己的版本:ValidateAntiforgeryTokenAuthorizationFilter
public class ValidateAjaxAntiforgeryTokenAuthorizationFilter : IAsyncAuthorizationFilter, IAntiforgeryPolicy
{
private readonly IAntiforgery _antiforgery;
private readonly ILogger _logger;
public ValidateAjaxAntiforgeryTokenAuthorizationFilter(IAntiforgery antiforgery, ILoggerFactory loggerFactory)
{
if (antiforgery == null)
{
throw new ArgumentNullException(nameof(antiforgery));
}
_antiforgery = antiforgery;
_logger = loggerFactory.CreateLogger<ValidateAjaxAntiforgeryTokenAuthorizationFilter>();
}
public async Task OnAuthorizationAsync(AuthorizationContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (IsClosestAntiforgeryPolicy(context.Filters) && ShouldValidate(context))
{
try
{
await _antiforgery.ValidateRequestAsync(context.HttpContext);
}
catch (AjaxAntiforgeryValidationException exception)
{
_logger.LogInformation(1, string.Concat("Ajax Antiforgery token validation failed. ", exception.Message));
context.Result = new BadRequestResult();
}
}
}
protected virtual bool ShouldValidate(AuthorizationContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
return true;
}
private bool IsClosestAntiforgeryPolicy(IList<IFilterMetadata> filters)
{
// Determine if this instance is the 'effective' antiforgery policy.
for (var i = filters.Count - 1; i >= 0; i--)
{
var filter = filters[i];
if (filter is IAntiforgeryPolicy)
{
return object.ReferenceEquals(this, filter);
}
}
Debug.Fail("The current instance should be in the list of filters.");
return false;
}
}
然而,我找不到包含IAntiforgeryPolicy的正确Nuget包和命名空间。虽然我在GitHub上找到了接口,但我应该在哪个包中找到它呢?
我的下一个尝试是去寻找IAntiforgery注入,并将DefaultAntiforgery替换为我的AjaxAntiforgery
。
public class AjaxAntiforgery : DefaultAntiforgery
{
private readonly AntiforgeryOptions _options;
private readonly IAntiforgeryTokenGenerator _tokenGenerator;
private readonly IAntiforgeryTokenSerializer _tokenSerializer;
private readonly IAntiforgeryTokenStore _tokenStore;
private readonly ILogger<AjaxAntiforgery> _logger;
public AjaxAntiforgery(
IOptions<AntiforgeryOptions> antiforgeryOptionsAccessor,
IAntiforgeryTokenGenerator tokenGenerator,
IAntiforgeryTokenSerializer tokenSerializer,
IAntiforgeryTokenStore tokenStore,
ILoggerFactory loggerFactory)
{
_options = antiforgeryOptionsAccessor.Value;
_tokenGenerator = tokenGenerator;
_tokenSerializer = tokenSerializer;
_tokenStore = tokenStore;
_logger = loggerFactory.CreateLogger<AjaxAntiforgery>();
}
}
由于ILoggerFactory
上没有通用方法CreateLogger<T>()
,我在这里被卡住了。 DefaultAntiforgery
的源代码有Microsoft.Extensions.Options
,但我找不到任何Nuget包中有该命名空间。存在Microsoft.Extensions.OptionsModel
,但这只引入了IOptions<out TOptions>
接口。
至于后续步骤,一旦我使授权过滤器正常工作,或获得一个新的IAntiforgery
实现,我应该在何处或如何将其注册到依赖注入以仅用于我将接受Ajax请求的操作?