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 |
public class AbpAntiForgeryOptions |
||||
{ |
{ |
||||
/// <summary>
|
/// <summary>
|
||||
/// Get/sets cookie name to transfer Anti Forgery token between server and client.
|
/// Use to set the cookie options to transfer Anti Forgery token between server and client.
|
||||
/// Default value: "XSRF-TOKEN".
|
/// Default name of the cookie: "XSRF-TOKEN".
|
||||
/// </summary>
|
/// </summary>
|
||||
public string TokenCookieName { get; set; } |
public CookieBuilder TokenCookie { get; } |
||||
|
|
||||
/// <summary>
|
/// <summary>
|
||||
/// Get/sets header name to transfer Anti Forgery token from client to the server.
|
/// Used to find auth cookie when validating Anti Forgery token.
|
||||
/// Default value: "X-XSRF-TOKEN".
|
/// Default value: "Identity.Application".
|
||||
/// </summary>
|
/// </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() |
public AbpAntiForgeryOptions() |
||||
{ |
{ |
||||
TokenCookieName = "XSRF-TOKEN"; |
AutoValidateFilter = type => true; |
||||
TokenHeaderName = "X-XSRF-TOKEN"; |
|
||||
|
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