mirror of https://github.com/abpframework/abp.git
committed by
GitHub
16 changed files with 354 additions and 26 deletions
@ -0,0 +1,35 @@ |
|||
using Microsoft.AspNetCore.Authentication.Cookies; |
|||
using Microsoft.Extensions.Options; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.AntiForgery |
|||
{ |
|||
public class AbpAntiForgeryCookieNameProvider : ITransientDependency |
|||
{ |
|||
private readonly IOptionsSnapshot<CookieAuthenticationOptions> _namedOptionsAccessor; |
|||
private readonly AbpAntiForgeryOptions _abpAntiForgeryOptions; |
|||
|
|||
public AbpAntiForgeryCookieNameProvider( |
|||
IOptionsSnapshot<CookieAuthenticationOptions> namedOptionsAccessor, |
|||
IOptions<AbpAntiForgeryOptions> abpAntiForgeryOptions) |
|||
{ |
|||
_namedOptionsAccessor = namedOptionsAccessor; |
|||
_abpAntiForgeryOptions = abpAntiForgeryOptions.Value; |
|||
} |
|||
|
|||
public virtual string GetAuthCookieNameOrNull() |
|||
{ |
|||
if (_abpAntiForgeryOptions.AuthCookieSchemaName == null) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
return _namedOptionsAccessor.Get(_abpAntiForgeryOptions.AuthCookieSchemaName)?.Cookie?.Name; |
|||
} |
|||
|
|||
public virtual string GetAntiForgeryCookieNameOrNull() |
|||
{ |
|||
return _abpAntiForgeryOptions.TokenCookie.Name; |
|||
} |
|||
} |
|||
} |
|||
@ -1,23 +1,68 @@ |
|||
namespace Volo.Abp.AspNetCore.Mvc.AntiForgery |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using JetBrains.Annotations; |
|||
using Microsoft.AspNetCore.Http; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.AntiForgery |
|||
{ |
|||
public class AbpAntiForgeryOptions |
|||
{ |
|||
/// <summary>
|
|||
/// Get/sets cookie name to transfer Anti Forgery token between server and client.
|
|||
/// Default value: "XSRF-TOKEN".
|
|||
/// Use to set the cookie options to transfer Anti Forgery token between server and client.
|
|||
/// Default name of the cookie: "XSRF-TOKEN".
|
|||
/// </summary>
|
|||
public string TokenCookieName { get; set; } |
|||
public CookieBuilder TokenCookie { get; } |
|||
|
|||
/// <summary>
|
|||
/// Get/sets header name to transfer Anti Forgery token from client to the server.
|
|||
/// Default value: "X-XSRF-TOKEN".
|
|||
/// Used to find auth cookie when validating Anti Forgery token.
|
|||
/// Default value: "Identity.Application".
|
|||
/// </summary>
|
|||
public string TokenHeaderName { get; set; } |
|||
public string AuthCookieSchemaName { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Default value: true.
|
|||
/// </summary>
|
|||
public bool AutoValidate { get; set; } = true; |
|||
|
|||
/// <summary>
|
|||
/// A predicate to filter types to auto-validate.
|
|||
/// Return true to select the type to validate.
|
|||
/// Default: returns true for all given types.
|
|||
/// </summary>
|
|||
[NotNull] |
|||
public Predicate<Type> AutoValidateFilter |
|||
{ |
|||
get => _autoValidateFilter; |
|||
set => _autoValidateFilter = Check.NotNull(value, nameof(value)); |
|||
} |
|||
private Predicate<Type> _autoValidateFilter; |
|||
|
|||
/// <summary>
|
|||
/// Default methods: "GET", "HEAD", "TRACE", "OPTIONS".
|
|||
/// </summary>
|
|||
[NotNull] |
|||
public HashSet<string> AutoValidateIgnoredHttpMethods |
|||
{ |
|||
get => _autoValidateIgnoredHttpMethods; |
|||
set => _autoValidateIgnoredHttpMethods = Check.NotNull(value, nameof(value)); |
|||
} |
|||
private HashSet<string> _autoValidateIgnoredHttpMethods; |
|||
|
|||
public AbpAntiForgeryOptions() |
|||
{ |
|||
TokenCookieName = "XSRF-TOKEN"; |
|||
TokenHeaderName = "X-XSRF-TOKEN"; |
|||
AutoValidateFilter = type => true; |
|||
|
|||
TokenCookie = new CookieBuilder |
|||
{ |
|||
Name = "XSRF-TOKEN", |
|||
HttpOnly = false, |
|||
IsEssential = true, |
|||
Expiration = TimeSpan.FromDays(3650) //10 years!
|
|||
}; |
|||
|
|||
AuthCookieSchemaName = "Identity.Application"; |
|||
|
|||
AutoValidateIgnoredHttpMethods = new HashSet<string> {"GET", "HEAD", "TRACE", "OPTIONS"}; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -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,65 @@ |
|||
using System; |
|||
using Microsoft.AspNetCore.Antiforgery; |
|||
using Microsoft.AspNetCore.Mvc.Abstractions; |
|||
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 AbpAntiForgeryOptions _options; |
|||
|
|||
public AbpAutoValidateAntiforgeryTokenAuthorizationFilter( |
|||
IAntiforgery antiforgery, |
|||
AbpAntiForgeryCookieNameProvider antiForgeryCookieNameProvider, |
|||
IOptions<AbpAntiForgeryOptions> options, |
|||
ILogger<AbpValidateAntiforgeryTokenAuthorizationFilter> logger) |
|||
: base( |
|||
antiforgery, |
|||
antiForgeryCookieNameProvider, |
|||
logger) |
|||
{ |
|||
_options = options.Value; |
|||
} |
|||
|
|||
protected override bool ShouldValidate(AuthorizationFilterContext context) |
|||
{ |
|||
if (!_options.AutoValidate) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
if(context.ActionDescriptor.IsControllerAction()) |
|||
{ |
|||
var controllerType = context.ActionDescriptor |
|||
.AsControllerActionDescriptor() |
|||
.ControllerTypeInfo |
|||
.AsType(); |
|||
|
|||
if (!_options.AutoValidateFilter(controllerType)) |
|||
{ |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
if (IsIgnoredHttpMethod(context)) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
return base.ShouldValidate(context); |
|||
} |
|||
|
|||
protected virtual bool IsIgnoredHttpMethod(AuthorizationFilterContext context) |
|||
{ |
|||
return context.HttpContext |
|||
.Request |
|||
.Method |
|||
.ToUpperInvariant() |
|||
.IsIn(_options.AutoValidateIgnoredHttpMethods); |
|||
} |
|||
} |
|||
} |
|||
@ -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,81 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Antiforgery; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Microsoft.AspNetCore.Mvc.Filters; |
|||
using Microsoft.AspNetCore.Mvc.ViewFeatures; |
|||
using Microsoft.Extensions.Logging; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.AntiForgery |
|||
{ |
|||
public class AbpValidateAntiforgeryTokenAuthorizationFilter : IAsyncAuthorizationFilter, IAntiforgeryPolicy, ITransientDependency |
|||
{ |
|||
private IAntiforgery _antiforgery; |
|||
private readonly AbpAntiForgeryCookieNameProvider _antiForgeryCookieNameProvider; |
|||
private readonly ILogger<AbpValidateAntiforgeryTokenAuthorizationFilter> _logger; |
|||
|
|||
public AbpValidateAntiforgeryTokenAuthorizationFilter( |
|||
IAntiforgery antiforgery, |
|||
AbpAntiForgeryCookieNameProvider antiForgeryCookieNameProvider, |
|||
ILogger<AbpValidateAntiforgeryTokenAuthorizationFilter> logger) |
|||
{ |
|||
_antiforgery = antiforgery; |
|||
_logger = logger; |
|||
_antiForgeryCookieNameProvider = antiForgeryCookieNameProvider; |
|||
} |
|||
|
|||
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) |
|||
{ |
|||
var authCookieName = _antiForgeryCookieNameProvider.GetAuthCookieNameOrNull(); |
|||
|
|||
//Always perform antiforgery validation when request contains authentication cookie
|
|||
if (authCookieName != null && |
|||
context.HttpContext.Request.Cookies.ContainsKey(authCookieName)) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
var antiForgeryCookieName = _antiForgeryCookieNameProvider.GetAntiForgeryCookieNameOrNull(); |
|||
|
|||
//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 (antiForgeryCookieName != null && |
|||
!context.HttpContext.Request.Cookies.ContainsKey(antiForgeryCookieName)) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
// Anything else requires a token.
|
|||
return true; |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue