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