dro*_*ani 12 c# ajax antiforgerytoken asp.net-core-mvc dnx
我一直在尝试重新创建ValidateAntiForgeryToken的Ajax版本 - 有许多关于如何为以前版本的MVC执行此操作的博客文章,但是对于最新的MVC 6,没有任何代码是相关的.不过,我要追求的核心原则是验证Cookie和Header __RequestVerificationToken,而不是将Cookie与表单值进行比较.我使用的是MVC 6.0.0-rc1-final,dnx451框架,所有的Microsoft.Extensions库都是1.0.0-rc1-final.
我最初的想法是继承ValidateAntiForgeryTokenAttribute,但是看一下源代码,我需要返回自己的授权过滤器实现来让它看看标题.
[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>();
}
}
Run Code Online (Sandbox Code Playgroud)
因此,我随后制作了自己的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;
}
}
Run Code Online (Sandbox Code Playgroud)
但是,我找不到包含IAntiforgeryPolicy的正确Nuget包和命名空间.虽然我在GitHub上找到了界面 - 我在哪个包中找到它?
我的下一次尝试是在IAntiforgery注入之后,用我自己的替换DefaultAntiforgeryAjaxAntiforgery.
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>();
}
}
Run Code Online (Sandbox Code Playgroud)
我能走到今天之前,我停滞了,因为有上没有通用的方法ILoggerFactory进行CreateLogger<T>().DefaultAntiforgeryhas 的源代码Microsoft.Extensions.Options,但我无法在任何Nuget包中找到该命名空间.Microsoft.Extensions.OptionsModel存在,但只是带来了IOptions<out TOptions>界面.
要完成所有这些,一旦我确实让Authorization Filter工作,或者我得到一个新的实现IAntiforgery,在哪里或如何使用依赖注入来注册它以使用它 - 并且仅用于我将接受的操作Ajax请求?
Joã*_*que 11
我有类似的问题.我不知道在.NET中是否会有任何变化,但是,当时,我在Startup.cs中添加了以下行到ConfigureServices方法,在行service.AddMvc()之前,为了验证发送的AntiForgeryToken通过Ajax:
services.AddAntiforgery(options =>
{
options.CookieName = "yourChosenCookieName";
options.HeaderName = "RequestVerificationToken";
});
Run Code Online (Sandbox Code Playgroud)
AJAX调用类似于以下内容:
var token = $('input[type=hidden][name=__RequestVerificationToken]', document).val();
var request = $.ajax({
data: { 'yourField': 'yourValue' },
...
headers: { 'RequestVerificationToken': token }
});
Run Code Online (Sandbox Code Playgroud)
然后,只需在Actions中使用native属性[ValidadeAntiForgeryToken].
我一直在努力解决类似的情况,将角度 POST 与 MVC6 连接起来,并提出了以下建议。
有两个问题需要解决:将安全令牌放入 MVC 的防伪验证子系统中,以及将 Angular 的 JSON 格式的回发数据转换为 MVC 模型。
我通过在 Startup.Configure() 中插入一些自定义中间件来处理第一步。中间件类非常简单:
public static class UseAngularXSRFExtension
{
public const string XSRFFieldName = "X-XSRF-TOKEN";
public static IApplicationBuilder UseAngularXSRF( this IApplicationBuilder builder )
{
return builder.Use( next => context =>
{
switch( context.Request.Method.ToLower() )
{
case "post":
case "put":
case "delete":
if( context.Request.Headers.ContainsKey( XSRFFieldName ) )
{
var formFields = new Dictionary<string, StringValues>()
{
{ XSRFFieldName, context.Request.Headers[XSRFFieldName] }
};
// this assumes that any POST, PUT or DELETE having a header
// which includes XSRFFieldName is coming from angular, so
// overwriting context.Request.Form is okay (since it's not
// being parsed by MVC's internals anyway)
context.Request.Form = new FormCollection( formFields );
}
break;
}
return next( context );
} );
}
}
Run Code Online (Sandbox Code Playgroud)
您可以在 Startup.Configure() 方法中使用以下行将其插入管道中:
app.UseAngularXSRF();
Run Code Online (Sandbox Code Playgroud)
我在调用 app.UseMVC() 之前执行了此操作。
请注意,此扩展会在其存在的任何 POST、PUT 或 DELETE 上传输 XSRF 标头,并通过覆盖现有表单字段集合来实现这一点。这符合我的设计模式——XSRF 标头出现在请求中的唯一情况是它来自我编写的某些角度代码——但它可能不适合您的设计模式。
我还认为您需要配置防伪子系统以使用 XSRF 字段名称的正确名称(我不确定默认值是什么)。您可以通过将以下行插入 Startup.ConfigureServices() 来完成此操作:
services.ConfigureAntiforgery( options => options.FormFieldName = UseAngularXSRFExtension.XSRFFieldName );
Run Code Online (Sandbox Code Playgroud)
我将其插入到 services.AddAntiforgery() 行之前。
有多种方法可以将 XSRF 令牌放入请求流中。我所做的是将以下内容添加到视图中:
...top of view...
@inject Microsoft.AspNet.Antiforgery.IAntiforgery af
...rest of view...
...inside the angular function...
var postHeaders = {
'X-XSRF-TOKEN': '@(af.GetTokens(this.Context).FormToken)',
'Content-Type': 'application/json; charset=utf-8',
};
$http.post( '/Dataset/DeleteDataset', JSON.stringify({ 'siteID': siteID }),
{
headers: postHeaders,
})
...rest of view...
Run Code Online (Sandbox Code Playgroud)
第二部分——翻译 JSON 数据——通过使用 [FromBody] 装饰操作方法上的模型类来处理:
// the [FromBody] attribute on the model -- and a class model, rather than a
// single integer model -- are necessary so that MVC can parse the JSON-formatted
// text POSTed by angular
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult DeleteDataset( [FromBody] DeleteSiteViewModel model )
{
}
Run Code Online (Sandbox Code Playgroud)
[FromBody] 仅适用于类实例。即使在我的例子中,我感兴趣的只是一个整数,我仍然必须虚拟一个类,它只包含一个整数属性。
希望这可以帮助。