Comment créez-vous un AuthorizeAtsortingbute personnalisé dans ASP.NET Core?

J’essaie de créer un atsortingbut d’autorisation personnalisé dans ASP.NET Core. Dans les versions précédentes, il était possible de remplacer bool AuthorizeCore(HttpContextBase httpContext) . Mais cela n’existe plus dans AuthorizeAtsortingbute .

Quelle est l’approche actuelle pour créer un AuthorizeAtsortingbute personnalisé?

Ce que j’essaie d’accomplir: je reçois un identifiant de session dans l’autorisation d’en-tête. À partir de cette identification, je saurai si une action particulière est valide.

L’approche recommandée par l’équipe ASP.Net Core consiste à utiliser la nouvelle conception de la politique qui est entièrement documentée ici . L’idée de base de cette nouvelle approche est d’utiliser le nouvel atsortingbut [Authorize] pour désigner une “politique” (par exemple, [Authorize( Policy = "YouNeedToBe18ToDoThis")] où la politique est enregistrée dans Startup.cs de l’application pour exécuter certains blocs. code (c.-à-d. s’assurer que l’utilisateur a une demande d’âge lorsque l’âge est 18 ans ou plus).

La conception de la politique est un excellent ajout au cadre et l’équipe ASP.Net Security Core doit être félicitée pour son introduction. Cela dit, il ne convient pas à tous les cas. L’inconvénient de cette approche est qu’elle ne fournit pas une solution pratique au besoin le plus courant de simplement affirmer qu’un contrôleur ou une action donnée nécessite un type de revendication donné. Dans le cas où une application peut avoir des centaines d’permissions discrètes régissant les opérations CRUD sur des ressources REST individuelles (“CanCreateOrder”, “CanReadOrder”, “CanUpdateOrder”, “CanDeleteOrder”, etc.), la nouvelle approche nécessite des opérations répétitives. un mappage entre un nom de politique et un nom de revendication (par exemple options.AddPolicy("CanUpdateOrder", policy => policy.RequireClaim(MyClaimTypes.Permission, "CanUpdateOrder)); ou écrire du code pour effectuer ces enregistrements au moment de l’exécution ( lire, par exemple, tous les types de réclamations à partir d’une firebase database et exécuter l’appel susmentionné en boucle. Le problème avec cette approche dans la majorité des cas est que cela ne nécessite pas de frais supplémentaires.

Alors que l’équipe ASP.Net Core Security recommande de ne jamais créer votre propre solution, c’est parfois l’option la plus prudente pour démarrer.

Ce qui suit est une implémentation qui utilise IAuthorizationFilter pour fournir un moyen simple d’exprimer une exigence de réclamation pour un contrôleur ou une action donnée:

 public class ClaimRequirementAtsortingbute : TypeFilterAtsortingbute { public ClaimRequirementAtsortingbute(ssortingng claimType, ssortingng claimValue) : base(typeof(ClaimRequirementFilter)) { Arguments = new object[] {new Claim(claimType, claimValue) }; } } public class ClaimRequirementFilter : IAuthorizationFilter { readonly Claim _claim; public ClaimRequirementFilter(Claim claim) { _claim = claim; } public void OnAuthorization(AuthorizationFilterContext context) { var hasClaim = context.HttpContext.User.Claims.Any(c => c.Type == _claim.Type && c.Value == _claim.Value); if (!hasClaim) { context.Result = new ForbidResult(); } } } [Route("api/resource")] public class MyController : Controller { [ClaimRequirement(MyClaimTypes.Permission, "CanReadResource")] [HttpGet] public IActionResult GetResource() { return Ok(); } } 

Je suis la personne de sécurité asp.net. Tout d’abord, permettez-moi de m’excuser que rien de tout cela ne soit documenté en dehors de l’échantillon de musicstore ou des tests unitaires, et tout est encore affiné en termes d’API exposées. La documentation détaillée est ici .

Nous ne voulons pas que vous écriviez des atsortingbuts d’autorisation personnalisés. Si vous devez le faire, nous avons fait quelque chose de mal. Au lieu de cela, vous devriez écrire des conditions d’ autorisation.

L’autorisation agit sur les identités. Les identités sont créées par authentification.

Vous dites dans les commentaires que vous voulez vérifier un identifiant de session dans un en-tête. Votre identifiant de session serait la base d’une identité. Si vous souhaitez utiliser l’atsortingbut Authorize vous devez écrire un middleware d’authentification pour prendre cet en-tête et le transformer en un object ClaimsPrincipal authentifié. Vous devriez alors vérifier cela dans une exigence d’autorisation. Les exigences d’autorisation peuvent être aussi compliquées que vous le souhaitez, par exemple, voici une revendication de date de naissance sur l’identité actuelle et autorisera si l’utilisateur a plus de 18 ans;

 public class Over18Requirement : AuthorizationHandler, IAuthorizationRequirement { public override void Handle(AuthorizationHandlerContext context, Over18Requirement requirement) { if (!context.User.HasClaim(c => c.Type == ClaimTypes.DateOfBirth)) { context.Fail(); return; } var dateOfBirth = Convert.ToDateTime(context.User.FindFirst(c => c.Type == ClaimTypes.DateOfBirth).Value); int age = DateTime.Today.Year - dateOfBirth.Year; if (dateOfBirth > DateTime.Today.AddYears(-age)) { age--; } if (age >= 18) { context.Succeed(requirement); } else { context.Fail(); } } } } 

Ensuite, dans votre fonction ConfigureServices() , vous le connectez

 services.AddAuthorization(options => { options.AddPolicy("Over18", policy => policy.Requirements.Add(new Authorization.Over18Requirement())); }); 

Et enfin l’appliquer à un contrôleur ou à une méthode d’action avec

 [Authorize(Policy = "Over18")] 

Il semble qu’avec ASP.NET Core 2, vous pouvez à nouveau hériter de AuthorizeAtsortingbute , vous devez juste implémenter IAuthorizationFilter :

 [AtsortingbuteUsage(AtsortingbuteTargets.Class | AtsortingbuteTargets.Method, AllowMultiple = true, Inherited = true)] public class CustomAuthorizeAtsortingbute : AuthorizeAtsortingbute, IAuthorizationFilter { private readonly ssortingng _someFilterParameter; public CustomAuthorizeAtsortingbute(ssortingng someFilterParameter) { _someFilterParameter = someFilterParameter; } public void OnAuthorization(AuthorizationFilterContext context) { var user = context.HttpContext.User; if (!user.Identity.IsAuthenticated) { // it isn't needed to set unauthorized result // as the base class already requires the user to be authenticated // this also makes redirect to a login page work properly // context.Result = new UnauthorizedResult(); return; } // you can also use registered services var someService = context.HttpContext.RequestServices.GetService(); var isAuthorized = someService.IsUserAuthorized(user.Identity.Name, _someFilterParameter); if (!isAuthorized) { context.Result = new StatusCodeResult((int)System.Net.HttpStatusCode.Forbidden); return; } } } 

Quelle est l’approche actuelle pour créer un AuthorizeAtsortingbute personnalisé?

Facile: ne créez pas votre propre AuthorizeAtsortingbute .

Pour les scénarios d’autorisation pure (comme la ressortingction de l’access à des utilisateurs spécifiques uniquement), l’approche recommandée consiste à utiliser le nouveau bloc d’autorisation: https://github.com/aspnet/MusicStore/blob/1c0aeb08bb1ebd846726232226279bbe001782e1/samples/MusicStore/Startup.cs#L84 -L92

 public class Startup { public void ConfigureServices(IServiceCollection services) { services.Configure(options => { options.AddPolicy("ManageStore", policy => policy.RequireClaim("Action", "ManageStore")); }); } } public class StoreController : Controller { [Authorize(Policy = "ManageStore"), HttpGet] public async Task Manage() { ... } } 

Pour l’authentification, il est préférable de le gérer au niveau du middleware.

Qu’est-ce que vous essayez de réaliser exactement?

Vous pouvez créer votre propre AuthorizationHandler qui trouvera des atsortingbuts personnalisés sur vos contrôleurs et actions et les transmettra à la méthode HandleRequirementAsync.

 public abstract class AtsortingbuteAuthorizationHandler : AuthorizationHandler where TRequirement : IAuthorizationRequirement where TAtsortingbute : Atsortingbute { protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, TRequirement requirement) { var atsortingbutes = new List(); var action = (context.Resource as AuthorizationFilterContext)?.ActionDescriptor as ControllerActionDescriptor; if (action != null) { atsortingbutes.AddRange(GetAtsortingbutes(action.ControllerTypeInfo.UnderlyingSystemType)); atsortingbutes.AddRange(GetAtsortingbutes(action.MethodInfo)); } return HandleRequirementAsync(context, requirement, atsortingbutes); } protected abstract Task HandleRequirementAsync(AuthorizationHandlerContext context, TRequirement requirement, IEnumerable atsortingbutes); private static IEnumerable GetAtsortingbutes(MemberInfo memberInfo) { return memberInfo.GetCustomAtsortingbutes(typeof(TAtsortingbute), false).Cast(); } } 

Vous pouvez ensuite l’utiliser pour tous les atsortingbuts personnalisés dont vous avez besoin sur vos contrôleurs ou vos actions. Par exemple, pour append des exigences d’autorisation. Créez simplement votre atsortingbut personnalisé.

 [AtsortingbuteUsage(AtsortingbuteTargets.Class | AtsortingbuteTargets.Method, AllowMultiple = true)] public class PermissionAtsortingbute : AuthorizeAtsortingbute { public ssortingng Name { get; } public PermissionAtsortingbute(ssortingng name) : base("Permission") { Name = name; } } 

Ensuite, créez une exigence à append à votre politique

 public class PermissionAuthorizationRequirement : IAuthorizationRequirement { //Add any custom requirement properties if you have them } 

Créez ensuite le AuthorizationHandler pour votre atsortingbut personnalisé, en héritant du gestionnaire d’atsortingbuts AtsortingbuteAuthorizationHandler que nous avons créé précédemment. Il sera passé un IEnumerable pour tous vos atsortingbuts personnalisés dans la méthode HandleRequirementsAsync, accumulés à partir de votre contrôleur et de votre action.

 public class PermissionAuthorizationHandler : AtsortingbuteAuthorizationHandler { protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionAuthorizationRequirement requirement, IEnumerable atsortingbutes) { foreach (var permissionAtsortingbute in atsortingbutes) { if (!await AuthorizeAsync(context.User, permissionAtsortingbute.Name)) { return; } } context.Succeed(requirement); } private Task AuthorizeAsync(ClaimsPrincipal user, ssortingng permission) { //Implement your custom user permission logic here } } 

Et enfin, dans votre méthode Startup.cs ConfigureServices, ajoutez votre AuthorizationHandler personnalisé aux services et ajoutez votre stratégie.

  services.AddSingleton(); services.AddAuthorization(options => { options.AddPolicy("Permission", policyBuilder => { policyBuilder.Requirements.Add(new PermissionAuthorizationRequirement()); }); }); 

Maintenant, vous pouvez simplement décorer vos contrôleurs et actions avec votre atsortingbut personnalisé.

 [Permission("AccessCustomers")] public class CustomersController { [Permission("AddCustomer")] IActionResult AddCustomer([FromBody] Customer customer) { //Add customer } } 

Basé sur Derek Greer GREAT answer, je l’ai fait avec des énumérations.

Voici un exemple de mon code:

 public enum PermissionItem { User, Product, Contact, Review, Client } public enum PermissionAction { Read, Create, } public class AuthorizeAtsortingbute : TypeFilterAtsortingbute { public AuthorizeAtsortingbute(PermissionItem item, PermissionAction action) : base(typeof(AuthorizeActionFilter)) { Arguments = new object[] { item, action }; } } public class AuthorizeActionFilter : IAsyncActionFilter { private readonly PermissionItem _item; private readonly PermissionAction _action; public AuthorizeActionFilter(PermissionItem item, PermissionAction action) { _item = item; _action = action; } public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { bool isAuthorized = MumboJumboFunction(context.HttpContext.User, _item, _action); // :) if (!isAuthorized) { context.Result = new UnauthorizedResult(); } else { await next(); } } } public class UserController : BaseController { private readonly DbContext _context; public UserController( DbContext context) : base() { _logger = logger; } [Authorize(PermissionItem.User, PermissionAction.Read)] public async Task Index() { return View(await _context.User.ToListAsync()); } }