mirror of https://github.com/abpframework/abp.git
8 changed files with 277 additions and 7 deletions
@ -0,0 +1,10 @@ |
|||||
|
namespace Volo.Abp.AspNetCore.Mvc.AntiForgery |
||||
|
{ |
||||
|
public class AbpAntiForgeryPreOptions |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Default value: true.
|
||||
|
/// </summary>
|
||||
|
public bool AutoValidate { get; set; } = true; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,37 @@ |
|||||
|
using System; |
||||
|
using Microsoft.AspNetCore.Mvc.Filters; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Mvc.AntiForgery |
||||
|
{ |
||||
|
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] |
||||
|
public class AbpAutoValidateAntiforgeryTokenAttribute : Attribute, IFilterFactory, IOrderedFilter |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Gets the order value for determining the order of execution of filters. Filters execute in
|
||||
|
/// ascending numeric value of the <see cref="Order"/> property.
|
||||
|
/// </summary>
|
||||
|
/// <remarks>
|
||||
|
/// <para>
|
||||
|
/// Filters are executed in a sequence determined by an ascending sort of the <see cref="Order"/> property.
|
||||
|
/// </para>
|
||||
|
/// <para>
|
||||
|
/// The default Order for this attribute is 1000 because it must run after any filter which does authentication
|
||||
|
/// or login in order to allow them to behave as expected (ie Unauthenticated or Redirect instead of 400).
|
||||
|
/// </para>
|
||||
|
/// <para>
|
||||
|
/// Look at <see cref="IOrderedFilter.Order"/> for more detailed info.
|
||||
|
/// </para>
|
||||
|
/// </remarks>
|
||||
|
public int Order { get; set; } = 1000; |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public bool IsReusable => true; |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) |
||||
|
{ |
||||
|
return serviceProvider.GetRequiredService<AbpAutoValidateAntiforgeryTokenAuthorizationFilter>(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,78 @@ |
|||||
|
using System; |
||||
|
using Microsoft.AspNetCore.Antiforgery; |
||||
|
using Microsoft.AspNetCore.Authentication.Cookies; |
||||
|
using Microsoft.AspNetCore.Mvc.Filters; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Mvc.AntiForgery |
||||
|
{ |
||||
|
public class AbpAutoValidateAntiforgeryTokenAuthorizationFilter : AbpValidateAntiforgeryTokenAuthorizationFilter, ITransientDependency |
||||
|
{ |
||||
|
private readonly AntiforgeryOptions _antiforgeryOptions; |
||||
|
private readonly IOptionsSnapshot<CookieAuthenticationOptions> _namedOptionsAccessor; |
||||
|
private readonly AbpAntiForgeryOptions _abpAntiForgeryOptions; |
||||
|
|
||||
|
public AbpAutoValidateAntiforgeryTokenAuthorizationFilter( |
||||
|
IAntiforgery antiforgery, |
||||
|
IOptions<AntiforgeryOptions> antiforgeryOptions, |
||||
|
IOptions<AbpAntiForgeryOptions> abpAntiForgeryOptions, |
||||
|
IOptionsSnapshot<CookieAuthenticationOptions> namedOptionsAccessor, |
||||
|
ILogger logger) |
||||
|
: base(antiforgery, antiforgeryOptions, abpAntiForgeryOptions, namedOptionsAccessor, logger) |
||||
|
{ |
||||
|
_namedOptionsAccessor = namedOptionsAccessor; |
||||
|
_abpAntiForgeryOptions = abpAntiForgeryOptions.Value; |
||||
|
_antiforgeryOptions = antiforgeryOptions.Value; |
||||
|
} |
||||
|
|
||||
|
protected override bool ShouldValidate(AuthorizationFilterContext context) |
||||
|
{ |
||||
|
if (!ShouldValidateInternal(context)) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
var cookieAuthenticationOptions = _namedOptionsAccessor.Get(_abpAntiForgeryOptions.AuthorizationCookieName); |
||||
|
|
||||
|
//Always perform antiforgery validation when request contains authentication cookie
|
||||
|
if (cookieAuthenticationOptions?.Cookie.Name != null && |
||||
|
context.HttpContext.Request.Cookies.ContainsKey(cookieAuthenticationOptions.Cookie.Name)) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
//No need to validate if antiforgery cookie is not sent.
|
||||
|
//That means the request is sent from a non-browser client.
|
||||
|
//See https://github.com/aspnet/Antiforgery/issues/115
|
||||
|
if (!context.HttpContext.Request.Cookies.ContainsKey(_antiforgeryOptions.Cookie.Name)) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// Anything else requires a token.
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
private static bool ShouldValidateInternal(AuthorizationFilterContext context) |
||||
|
{ |
||||
|
if (context == null) |
||||
|
{ |
||||
|
throw new ArgumentNullException(nameof(context)); |
||||
|
} |
||||
|
|
||||
|
var method = context.HttpContext.Request.Method; |
||||
|
if (string.Equals("GET", method, StringComparison.OrdinalIgnoreCase) || |
||||
|
string.Equals("HEAD", method, StringComparison.OrdinalIgnoreCase) || |
||||
|
string.Equals("TRACE", method, StringComparison.OrdinalIgnoreCase) || |
||||
|
string.Equals("OPTIONS", method, StringComparison.OrdinalIgnoreCase)) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// Anything else requires a token.
|
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,37 @@ |
|||||
|
using System; |
||||
|
using Microsoft.AspNetCore.Mvc.Filters; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Mvc.AntiForgery |
||||
|
{ |
||||
|
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] |
||||
|
public class AbpValidateAntiForgeryTokenAttribute : Attribute, IFilterFactory, IOrderedFilter |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Gets the order value for determining the order of execution of filters. Filters execute in
|
||||
|
/// ascending numeric value of the <see cref="Order"/> property.
|
||||
|
/// </summary>
|
||||
|
/// <remarks>
|
||||
|
/// <para>
|
||||
|
/// Filters are executed in an ordering determined by an ascending sort of the <see cref="Order"/> property.
|
||||
|
/// </para>
|
||||
|
/// <para>
|
||||
|
/// The default Order for this attribute is 1000 because it must run after any filter which does authentication
|
||||
|
/// or login in order to allow them to behave as expected (ie Unauthenticated or Redirect instead of 400).
|
||||
|
/// </para>
|
||||
|
/// <para>
|
||||
|
/// Look at <see cref="IOrderedFilter.Order"/> for more detailed info.
|
||||
|
/// </para>
|
||||
|
/// </remarks>
|
||||
|
public int Order { get; set; } = 1000; |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public bool IsReusable => true; |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) |
||||
|
{ |
||||
|
return serviceProvider.GetRequiredService<AbpValidateAntiforgeryTokenAuthorizationFilter>(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,100 @@ |
|||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.AspNetCore.Antiforgery; |
||||
|
using Microsoft.AspNetCore.Authentication.Cookies; |
||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using Microsoft.AspNetCore.Mvc.Filters; |
||||
|
using Microsoft.AspNetCore.Mvc.ViewFeatures; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Mvc.AntiForgery |
||||
|
{ |
||||
|
public class AbpValidateAntiforgeryTokenAuthorizationFilter : IAsyncAuthorizationFilter, IAntiforgeryPolicy |
||||
|
{ |
||||
|
private IAntiforgery _antiforgery; |
||||
|
private readonly AntiforgeryOptions _antiforgeryOptions; |
||||
|
private readonly IOptionsSnapshot<CookieAuthenticationOptions> _namedOptionsAccessor; |
||||
|
private readonly AbpAntiForgeryOptions _abpAntiForgeryOptions; |
||||
|
private readonly ILogger _logger; |
||||
|
|
||||
|
public AbpValidateAntiforgeryTokenAuthorizationFilter( |
||||
|
IAntiforgery antiforgery, |
||||
|
IOptions<AntiforgeryOptions> antiforgeryOptions, |
||||
|
IOptions<AbpAntiForgeryOptions> abpAntiForgeryOptions, |
||||
|
IOptionsSnapshot<CookieAuthenticationOptions> namedOptionsAccessor, |
||||
|
ILogger logger) |
||||
|
{ |
||||
|
_antiforgery = antiforgery; |
||||
|
_antiforgeryOptions = antiforgeryOptions.Value; |
||||
|
_namedOptionsAccessor = namedOptionsAccessor; |
||||
|
_logger = logger; |
||||
|
_abpAntiForgeryOptions = abpAntiForgeryOptions.Value; |
||||
|
} |
||||
|
|
||||
|
public async Task OnAuthorizationAsync(AuthorizationFilterContext context) |
||||
|
{ |
||||
|
if (context == null) |
||||
|
{ |
||||
|
throw new ArgumentNullException(nameof(context)); |
||||
|
} |
||||
|
|
||||
|
if (!context.IsEffectivePolicy<IAntiforgeryPolicy>(this)) |
||||
|
{ |
||||
|
_logger.LogInformation("Skipping the execution of current filter as its not the most effective filter implementing the policy " + typeof(IAntiforgeryPolicy)); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (ShouldValidate(context)) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
await _antiforgery.ValidateRequestAsync(context.HttpContext); |
||||
|
} |
||||
|
catch (AntiforgeryValidationException exception) |
||||
|
{ |
||||
|
_logger.LogError(exception.Message, exception); |
||||
|
context.Result = new AntiforgeryValidationFailedResult(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected virtual bool ShouldValidate(AuthorizationFilterContext context) |
||||
|
{ |
||||
|
if (!ShouldValidateInternal(context)) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
var cookieAuthenticationOptions = _namedOptionsAccessor.Get(_abpAntiForgeryOptions.AuthorizationCookieName); |
||||
|
|
||||
|
//Always perform antiforgery validation when request contains authentication cookie
|
||||
|
if (cookieAuthenticationOptions?.Cookie.Name != null && |
||||
|
context.HttpContext.Request.Cookies.ContainsKey(cookieAuthenticationOptions.Cookie.Name)) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
//No need to validate if antiforgery cookie is not sent.
|
||||
|
//That means the request is sent from a non-browser client.
|
||||
|
//See https://github.com/aspnet/Antiforgery/issues/115
|
||||
|
if (!context.HttpContext.Request.Cookies.ContainsKey(_antiforgeryOptions.Cookie.Name)) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// Anything else requires a token.
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
private static bool ShouldValidateInternal(AuthorizationFilterContext context) |
||||
|
{ |
||||
|
if (context == null) |
||||
|
{ |
||||
|
throw new ArgumentNullException(nameof(context)); |
||||
|
} |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue