Autorización basada en directivas en ASP.NET Core
En primer lugar, la autorización basada en roles y la autorización basada en notificaciones usan un requisito, un controlador de requisitos y una directiva preconfigurada. Estos bloques de creación admiten la expresión de evaluaciones de autorización en el código. El resultado es una estructura de autorización más completa, reutilizable y que se puede probar.
Una directiva de autorización consta de uno o varios requisitos. Regístrelo como parte de la configuración del servicio de autorización, en el archivo Program.cs de la aplicación:
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("AtLeast21", policy =>
policy.Requirements.Add(new MinimumAgeRequirement(21)));
});
En el ejemplo anterior, se crea una directiva "AtLeast21". Tiene un único requisito que es de una edad mínima, que se proporciona — como un parámetro para el requisito.
IAuthorizationService
El servicio principal que determina si la autorización es correcta es IAuthorizationService :
/// <summary>
/// Checks policy based permissions for a user
/// </summary>
public interface IAuthorizationService
{
/// <summary>
/// Checks if a user meets a specific set of requirements for the specified resource
/// </summary>
/// <param name="user">The user to evaluate the requirements against.</param>
/// <param name="resource">
/// An optional resource the policy should be checked with.
/// If a resource is not required for policy evaluation you may pass null as the value
/// </param>
/// <param name="requirements">The requirements to evaluate.</param>
/// <returns>
/// A flag indicating whether authorization has succeeded.
/// This value is <value>true</value> when the user fulfills the policy;
/// otherwise <value>false</value>.
/// </returns>
/// <remarks>
/// Resource is an optional parameter and may be null. Please ensure that you check
/// it is not null before acting upon it.
/// </remarks>
Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object resource,
IEnumerable<IAuthorizationRequirement> requirements);
/// <summary>
/// Checks if a user meets a specific authorization policy
/// </summary>
/// <param name="user">The user to check the policy against.</param>
/// <param name="resource">
/// An optional resource the policy should be checked with.
/// If a resource is not required for policy evaluation you may pass null as the value
/// </param>
/// <param name="policyName">The name of the policy to check against a specific
/// context.</param>
/// <returns>
/// A flag indicating whether authorization has succeeded.
/// Returns a flag indicating whether the user, and optional resource has fulfilled
/// the policy.
/// <value>true</value> when the policy has been fulfilled;
/// otherwise <value>false</value>.
/// </returns>
/// <remarks>
/// Resource is an optional parameter and may be null. Please ensure that you check
/// it is not null before acting upon it.
/// </remarks>
Task<AuthorizationResult> AuthorizeAsync(
ClaimsPrincipal user, object resource, string policyName);
}
El código anterior resalta los dos métodos de IAuthorizationService.
IAuthorizationRequirement es un servicio de marcadores sin métodos y el mecanismo para realizar un seguimiento de si la autorización es correcta.
Cada IAuthorizationHandler uno de ellos es responsable de comprobar si se cumplen los requisitos:
/// <summary>
/// Classes implementing this interface are able to make a decision if authorization
/// is allowed.
/// </summary>
public interface IAuthorizationHandler
{
/// <summary>
/// Makes a decision if authorization is allowed.
/// </summary>
/// <param name="context">The authorization information.</param>
Task HandleAsync(AuthorizationHandlerContext context);
}
La clase es lo que usa el controlador para marcar si se han cumplido AuthorizationHandlerContext los requisitos:
context.Succeed(requirement)
El código siguiente muestra la implementación predeterminada simplificada (y anotada con comentarios) del servicio de autorización:
public async Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user,
object resource, IEnumerable<IAuthorizationRequirement> requirements)
{
// Create a tracking context from the authorization inputs.
var authContext = _contextFactory.CreateContext(requirements, user, resource);
// By default this returns an IEnumerable<IAuthorizationHandlers> from DI.
var handlers = await _handlers.GetHandlersAsync(authContext);
// Invoke all handlers.
foreach (var handler in handlers)
{
await handler.HandleAsync(authContext);
}
// Check the context, by default success is when all requirements have been met.
return _evaluator.Evaluate(authContext);
}
El código siguiente muestra una configuración típica del servicio de autorización:
// Add all of your handlers to DI.
builder.Services.AddSingleton<IAuthorizationHandler, MyHandler1>();
// MyHandler2, ...
builder.Services.AddSingleton<IAuthorizationHandler, MyHandlerN>();
// Configure your policies
builder.Services.AddAuthorization(options =>
options.AddPolicy("Something",
policy => policy.RequireClaim("Permission", "CanViewPage", "CanViewAnything")));
Use IAuthorizationService , o para la [Authorize(Policy = "Something")] RequireAuthorization("Something") autorización.
Aplicación de directivas a controladores MVC
Para las aplicaciones que usan Razor Pages, consulte la sección Aplicar directivas a Razor Pages.
Aplique directivas a los controladores mediante el [Authorize] atributo con el nombre de la directiva. Por ejemplo:
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace AuthorizationPoliciesSample.Controllers;
[Authorize(Policy = "AtLeast21")]
public class AtLeast21Controller : Controller
{
public IActionResult Index() => View();
}
Aplicación de directivas a Razor Pages
Aplique directivas a Razor Pages mediante el atributo con el nombre de la [Authorize] directiva. Por ejemplo:
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace AuthorizationPoliciesSample.Pages;
[Authorize(Policy = "AtLeast21")]
public class AtLeast21Model : PageModel { }
Las directivas no se pueden aplicar en el nivel de controlador Razor de página, deben aplicarse a la página.
Las directivas también se pueden aplicar Razor a Pages mediante una convención de autorización.
Aplicación de directivas a puntos de conexión
Aplique directivas a los puntos de conexión mediante RequireAuthorization con el nombre de la directiva. Por ejemplo:
app.MapGet("/helloworld", () => "Hello World!")
.RequireAuthorization("AtLeast21");
Requisitos
Un requisito de autorización es una colección de parámetros de datos que una directiva puede usar para evaluar la entidad de seguridad de usuario actual. En nuestra directiva "AtLeast21", el requisito es un único parámetro — de la edad mínima. Un requisito implementa IAuthorizationRequirement,que es una interfaz de marcador vacía. Se podría implementar un requisito de edad mínima parametrizado de la siguiente manera:
using Microsoft.AspNetCore.Authorization;
namespace AuthorizationPoliciesSample.Policies.Requirements;
public class MinimumAgeRequirement : IAuthorizationRequirement
{
public MinimumAgeRequirement(int minimumAge) =>
MinimumAge = minimumAge;
public int MinimumAge { get; }
}
Si una directiva de autorización contiene varios requisitos de autorización, todos los requisitos deben pasar para que la evaluación de la directiva se haga correctamente. En otras palabras, varios requisitos de autorización agregados a una única directiva de autorización se tratan en función de AND.
Nota
No es necesario que un requisito tenga datos ni propiedades.
Controladores de autorización
Un controlador de autorización es responsable de la evaluación de las propiedades de un requisito. El controlador de autorización evalúa los requisitos con respecto a un AuthorizationHandlerContext proporcionado para determinar si se permite el acceso.
Un requisito puede tener varios controladores. Un controlador puede heredar AuthorizationHandler <TRequirement> , TRequirement donde es el requisito que se debe controlar. Como alternativa, un controlador puede implementar IAuthorizationHandler directamente para controlar más de un tipo de requisito.
Usar un controlador para un requisito
En el ejemplo siguiente se muestra una relación uno a uno en la que un controlador de edad mínima controla un único requisito:
using System.Security.Claims;
using AuthorizationPoliciesSample.Policies.Requirements;
using Microsoft.AspNetCore.Authorization;
namespace AuthorizationPoliciesSample.Policies.Handlers;
public class MinimumAgeHandler : AuthorizationHandler<MinimumAgeRequirement>
{
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context, MinimumAgeRequirement requirement)
{
var dateOfBirthClaim = context.User.FindFirst(
c => c.Type == ClaimTypes.DateOfBirth && c.Issuer == "http://contoso.com");
if (dateOfBirthClaim is null)
{
return Task.CompletedTask;
}
var dateOfBirth = Convert.ToDateTime(dateOfBirthClaim.Value);
int calculatedAge = DateTime.Today.Year - dateOfBirth.Year;
if (dateOfBirth > DateTime.Today.AddYears(-calculatedAge))
{
calculatedAge--;
}
if (calculatedAge >= requirement.MinimumAge)
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
El código anterior determina si la entidad de seguridad de usuario actual tiene una notificación de fecha de nacimiento emitida por un emisor conocido y de confianza. La autorización no puede producirse cuando falta la notificación, en cuyo caso se devuelve una tarea completada. Cuando hay una notificación, se calcula la edad del usuario. Si el usuario cumple la edad mínima definida por el requisito, la autorización se considera correcta. Cuando la autorización se realiza context.Succeed correctamente, se invoca con el requisito cumplido como su único parámetro.
Uso de un controlador para varios requisitos
En el ejemplo siguiente se muestra una relación uno a varios en la que un controlador de permisos puede controlar tres tipos diferentes de requisitos:
using System.Security.Claims;
using AuthorizationPoliciesSample.Policies.Requirements;
using Microsoft.AspNetCore.Authorization;
namespace AuthorizationPoliciesSample.Policies.Handlers;
public class PermissionHandler : IAuthorizationHandler
{
public Task HandleAsync(AuthorizationHandlerContext context)
{
var pendingRequirements = context.PendingRequirements.ToList();
foreach (var requirement in pendingRequirements)
{
if (requirement is ReadPermission)
{
if (IsOwner(context.User, context.Resource)
|| IsSponsor(context.User, context.Resource))
{
context.Succeed(requirement);
}
}
else if (requirement is EditPermission || requirement is DeletePermission)
{
if (IsOwner(context.User, context.Resource))
{
context.Succeed(requirement);
}
}
}
return Task.CompletedTask;
}
private static bool IsOwner(ClaimsPrincipal user, object? resource)
{
// Code omitted for brevity
return true;
}
private static bool IsSponsor(ClaimsPrincipal user, object? resource)
{
// Code omitted for brevity
return true;
}
}
El código anterior recorre PendingRequirementsuna propiedad que contiene requisitos — no marcados como correctos. Para un requisito, el usuario debe ser propietario o patrocinador ReadPermission para acceder al recurso solicitado. En el caso de un EditPermission requisito o , deben ser DeletePermission propietarios para acceder al recurso solicitado.
Registro del controlador
Registre controladores en la colección de servicios durante la configuración. Por ejemplo:
builder.Services.AddSingleton<IAuthorizationHandler, MinimumAgeHandler>();
El código anterior se registra MinimumAgeHandler como singleton. Los controladores se pueden registrar mediante cualquiera de las duraciones de servicio integradas.
Es posible agrupar un requisito y un controlador en una sola clase que implementa IAuthorizationRequirement y IAuthorizationHandler . Esto crea un acoplamiento estricto entre el controlador y el requisito y solo se recomienda para los requisitos y controladores simples. La creación de una clase que implementa ambas interfaces elimina la necesidad de registrar el controlador en la integración de integración de elementos debido al passThroughAuthorizationHandler integrado que permite que los requisitos se controlen por sí mismos.
Vea la clase AssertionRequirement para obtener un buen ejemplo en el que es un requisito y el controlador en una clase AssertionRequirement totalmente independiente.
¿Qué debe devolver un controlador?
Tenga en cuenta Handle que el método del ejemplo de controlador no devuelve ningún valor. ¿Cómo se indica un estado de éxito o error?
Un controlador indica que se ha realizado correctamente llamando a
context.Succeed(IAuthorizationRequirement requirement), pasando el requisito que se ha validado correctamente.Por lo general, no es necesario que un controlador controle los errores, ya que otros controladores para el mismo requisito pueden tener éxito.
Para garantizar el error, incluso si otros controladores de requisitos se realiza correctamente, llame a
context.Fail.
Si un controlador llama a o , todavía se llama a todos context.Succeed context.Fail los demás controladores. Esto permite que los requisitos produzcan efectos secundarios, como el registro, que tiene lugar incluso si otro controlador ha validado correctamente o no un requisito. Cuando se establece en false , la propiedad InvokeHandlersAfterFailure cortocircuiza la ejecución de controladores cuando context.Fail se llama a . InvokeHandlersAfterFailure el valor predeterminado es true , en cuyo caso se llama a todos los controladores.
Nota
Se llama a los controladores de autorización incluso si se produce un error en la autenticación.
¿Por qué quiero varios controladores para un requisito?
En los casos en los que quiera que la evaluación sea en función de OR, implemente varios controladores para un único requisito. Por ejemplo, Microsoft tiene puertas que solo se abren con tarjetas de clave. Si deja la tarjeta de la clave en casa, el recepcionista imprime un adhesivo temporal y abre la puerta. En este escenario, tendría un único requisito, BuildingEntry, pero varios controladores, cada uno de los que examina un único requisito.
BuildingEntryRequirement.cs
using Microsoft.AspNetCore.Authorization;
namespace AuthorizationPoliciesSample.Policies.Requirements;
public class BuildingEntryRequirement : IAuthorizationRequirement { }
BadgeEntryHandler.cs
using AuthorizationPoliciesSample.Policies.Requirements;
using Microsoft.AspNetCore.Authorization;
namespace AuthorizationPoliciesSample.Policies.Handlers;
public class BadgeEntryHandler : AuthorizationHandler<BuildingEntryRequirement>
{
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context, BuildingEntryRequirement requirement)
{
if (context.User.HasClaim(
c => c.Type == "BadgeId" && c.Issuer == "https://microsoftsecurity"))
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
TemporaryStickerHandler.cs
using AuthorizationPoliciesSample.Policies.Requirements;
using Microsoft.AspNetCore.Authorization;
namespace AuthorizationPoliciesSample.Policies.Handlers;
public class TemporaryStickerHandler : AuthorizationHandler<BuildingEntryRequirement>
{
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context, BuildingEntryRequirement requirement)
{
if (context.User.HasClaim(
c => c.Type == "TemporaryBadgeId" && c.Issuer == "https://microsoftsecurity"))
{
// Code to check expiration date omitted for brevity.
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
Asegúrese de que ambos controladores están registrados. Si alguno de los controladores se realiza correctamente cuando una directiva evalúa BuildingEntryRequirement , la evaluación de la directiva se realiza correctamente.
Uso de un func para cumplir una directiva
Puede haber situaciones en las que el cumplimiento de una directiva sea fácil de expresar en el código. Es posible proporcionar un al Func<AuthorizationHandlerContext, bool> configurar una directiva con el generador de RequireAssertion directivas.
Por ejemplo, el anterior BadgeEntryHandler se podría reescribir de la siguiente manera:
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("BadgeEntry", policy =>
policy.RequireAssertion(context => context.User.HasClaim(c =>
(c.Type == "BadgeId" || c.Type == "TemporaryBadgeId")
&& c.Issuer == "https://microsoftsecurity")));
});
Acceso al contexto de solicitud de MVC en controladores
El HandleRequirementAsync método tiene dos parámetros: y que se AuthorizationHandlerContext TRequirement controla. Los marcos como MVC o pueden agregar cualquier objeto a SignalR la propiedad en para pasar información Resource AuthorizationHandlerContext adicional.
Cuando se usa el enrutamiento de puntos de conexión, el middleware de autorización suele controlar la autorización. En este caso, la Resource propiedad es una instancia de HttpContext . El contexto se puede usar para acceder al punto de conexión actual, que se puede usar para sondear el recurso subyacente al que está enrutando. Por ejemplo:
if (context.Resource is HttpContext httpContext)
{
var endpoint = httpContext.GetEndpoint();
var actionDescriptor = endpoint.Metadata.GetMetadata<ControllerActionDescriptor>();
...
}
Con el enrutamiento tradicional, o cuando la autorización se produce como parte del filtro de autorización de MVC, el valor de Resource es una instancia de AuthorizationFilterContext . Esta propiedad proporciona acceso a HttpContext , y a todo lo demás proporcionado por MVC y RouteData Razor Pages.
El uso de la Resource propiedad es específico del marco. El uso de la información Resource de la propiedad limita las directivas de autorización a marcos concretos. Debe convertir la propiedad mediante la palabra clave y, a continuación, confirmar que la conversión se ha ejecutado correctamente para asegurarse de que el código no se bloquea con cuando se ejecuta en Resource is otros InvalidCastException marcos:
// Requires the following import:
// using Microsoft.AspNetCore.Mvc.Filters;
if (context.Resource is AuthorizationFilterContext mvcContext)
{
// Examine MVC-specific things like routing data.
}
Globalmente, se requiere que todos los usuarios se autentiquen
Para obtener información sobre cómo requerir globalmente la autenticación de todos los usuarios, vea Requerir usuarios autenticados.
En primer lugar, la autorización basada en roles y la autorización basada en notificaciones usan un requisito, un controlador de requisitos y una directiva preconfigurado. Estos bloques de creación admiten la expresión de evaluaciones de autorización en el código. El resultado es una estructura de autorización más enriquecible, reutilizable y que se puede probar.
Una directiva de autorización consta de uno o varios requisitos. Se registra como parte de la configuración del servicio de autorización, en el Startup.ConfigureServices método :
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddRazorPages();
services.AddAuthorization(options =>
{
options.AddPolicy("AtLeast21", policy =>
policy.Requirements.Add(new MinimumAgeRequirement(21)));
});
}
En el ejemplo anterior, se crea una directiva "AtLeast21". Tiene un único requisito — que tiene una antigüedad mínima, que se proporciona como parámetro para el requisito.
IAuthorizationService
El servicio principal que determina si la autorización se realiza correctamente es IAuthorizationService :
/// <summary>
/// Checks policy based permissions for a user
/// </summary>
public interface IAuthorizationService
{
/// <summary>
/// Checks if a user meets a specific set of requirements for the specified resource
/// </summary>
/// <param name="user">The user to evaluate the requirements against.</param>
/// <param name="resource">
/// An optional resource the policy should be checked with.
/// If a resource is not required for policy evaluation you may pass null as the value
/// </param>
/// <param name="requirements">The requirements to evaluate.</param>
/// <returns>
/// A flag indicating whether authorization has succeeded.
/// This value is <value>true</value> when the user fulfills the policy;
/// otherwise <value>false</value>.
/// </returns>
/// <remarks>
/// Resource is an optional parameter and may be null. Please ensure that you check
/// it is not null before acting upon it.
/// </remarks>
Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object resource,
IEnumerable<IAuthorizationRequirement> requirements);
/// <summary>
/// Checks if a user meets a specific authorization policy
/// </summary>
/// <param name="user">The user to check the policy against.</param>
/// <param name="resource">
/// An optional resource the policy should be checked with.
/// If a resource is not required for policy evaluation you may pass null as the value
/// </param>
/// <param name="policyName">The name of the policy to check against a specific
/// context.</param>
/// <returns>
/// A flag indicating whether authorization has succeeded.
/// Returns a flag indicating whether the user, and optional resource has fulfilled
/// the policy.
/// <value>true</value> when the policy has been fulfilled;
/// otherwise <value>false</value>.
/// </returns>
/// <remarks>
/// Resource is an optional parameter and may be null. Please ensure that you check
/// it is not null before acting upon it.
/// </remarks>
Task<AuthorizationResult> AuthorizeAsync(
ClaimsPrincipal user, object resource, string policyName);
}
El código anterior resalta los dos métodos de IAuthorizationService.
IAuthorizationRequirement es un servicio de marcador sin métodos y el mecanismo para realizar un seguimiento de si la autorización es correcta.
Cada IAuthorizationHandler uno de ellos es responsable de comprobar si se cumplen los requisitos:
/// <summary>
/// Classes implementing this interface are able to make a decision if authorization
/// is allowed.
/// </summary>
public interface IAuthorizationHandler
{
/// <summary>
/// Makes a decision if authorization is allowed.
/// </summary>
/// <param name="context">The authorization information.</param>
Task HandleAsync(AuthorizationHandlerContext context);
}
La AuthorizationHandlerContext clase es lo que usa el controlador para marcar si se han cumplido los requisitos:
context.Succeed(requirement)
El código siguiente muestra la implementación predeterminada simplificada (y anotada con comentarios) del servicio de autorización:
public async Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user,
object resource, IEnumerable<IAuthorizationRequirement> requirements)
{
// Create a tracking context from the authorization inputs.
var authContext = _contextFactory.CreateContext(requirements, user, resource);
// By default this returns an IEnumerable<IAuthorizationHandlers> from DI.
var handlers = await _handlers.GetHandlersAsync(authContext);
// Invoke all handlers.
foreach (var handler in handlers)
{
await handler.HandleAsync(authContext);
}
// Check the context, by default success is when all requirements have been met.
return _evaluator.Evaluate(authContext);
}
En el código siguiente se muestra un típico ConfigureServices :
public void ConfigureServices(IServiceCollection services)
{
// Add all of your handlers to DI.
services.AddSingleton<IAuthorizationHandler, MyHandler1>();
// MyHandler2, ...
services.AddSingleton<IAuthorizationHandler, MyHandlerN>();
// Configure your policies
services.AddAuthorization(options =>
options.AddPolicy("Something",
policy => policy.RequireClaim("Permission", "CanViewPage", "CanViewAnything")));
services.AddControllersWithViews();
services.AddRazorPages();
}
Use IAuthorizationService o para la [Authorize(Policy = "Something")] autorización.
Aplicación de directivas a controladores MVC
Si usa Pages, consulte Razor Aplicación de directivas a Razor páginas en este documento.
Las directivas se aplican a los controladores mediante el atributo [Authorize] con el nombre de directiva. Por ejemplo:
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
[Authorize(Policy = "AtLeast21")]
public class AlcoholPurchaseController : Controller
{
public IActionResult Index() => View();
}
Aplicación de directivas a Razor Pages
Las directivas se aplican Razor a Pages mediante el atributo con el nombre de [Authorize] directiva. Por ejemplo:
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;
[Authorize(Policy = "AtLeast21")]
public class AlcoholPurchaseModel : PageModel
{
}
Las directivas no se pueden aplicar en el nivel de controlador Razor de página, deben aplicarse a la página.
Las directivas se pueden aplicar a Razor Pages mediante una convención de autorización.
Requisitos
Un requisito de autorización es una colección de parámetros de datos que una directiva puede usar para evaluar la entidad de seguridad de usuario actual. En nuestra directiva "AtLeast21", el requisito es un único parámetro — de la antigüedad mínima. Un requisito implementa IAuthorizationRequirement,que es una interfaz de marcador vacía. Un requisito de antigüedad mínima parametrizada podría implementarse de la siguiente manera:
using Microsoft.AspNetCore.Authorization;
public class MinimumAgeRequirement : IAuthorizationRequirement
{
public int MinimumAge { get; }
public MinimumAgeRequirement(int minimumAge)
{
MinimumAge = minimumAge;
}
}
Si una directiva de autorización contiene varios requisitos de autorización, todos los requisitos deben pasar para que la evaluación de la directiva se haga correctamente. En otras palabras, varios requisitos de autorización agregados a una única directiva de autorización se tratan en función de AND.
Nota
No es necesario que un requisito tenga datos ni propiedades.
Controladores de autorización
Un controlador de autorización es responsable de la evaluación de las propiedades de un requisito. El controlador de autorización evalúa los requisitos con respecto a un AuthorizationHandlerContext proporcionado para determinar si se permite el acceso.
Un requisito puede tener varios controladores. Un controlador puede heredar AuthorizationHandler <TRequirement> , TRequirement donde es el requisito que se debe controlar. Como alternativa, un controlador puede implementar IAuthorizationHandler para controlar más de un tipo de requisito.
Usar un controlador para un requisito
A continuación se muestra un ejemplo de una relación uno a uno en la que un controlador de edad mínima utiliza un único requisito:
using System;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using PoliciesAuthApp1.Services.Requirements;
public class MinimumAgeHandler : AuthorizationHandler<MinimumAgeRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
MinimumAgeRequirement requirement)
{
if (!context.User.HasClaim(c => c.Type == ClaimTypes.DateOfBirth &&
c.Issuer == "http://contoso.com"))
{
//TODO: Use the following if targeting a version of
//.NET Framework older than 4.6:
// return Task.FromResult(0);
return Task.CompletedTask;
}
var dateOfBirth = Convert.ToDateTime(
context.User.FindFirst(c => c.Type == ClaimTypes.DateOfBirth &&
c.Issuer == "http://contoso.com").Value);
int calculatedAge = DateTime.Today.Year - dateOfBirth.Year;
if (dateOfBirth > DateTime.Today.AddYears(-calculatedAge))
{
calculatedAge--;
}
if (calculatedAge >= requirement.MinimumAge)
{
context.Succeed(requirement);
}
//TODO: Use the following if targeting a version of
//.NET Framework older than 4.6:
// return Task.FromResult(0);
return Task.CompletedTask;
}
}
El código anterior determina si la entidad de seguridad del usuario actual tiene una notificación de fecha de nacimiento emitida por un emisor conocido y de confianza. La autorización no puede producirse cuando falta la notificación, en cuyo caso se devuelve una tarea completada. Cuando hay una notificación, se calcula la edad del usuario. Si el usuario cumple la antigüedad mínima definida por el requisito, la autorización se considera correcta. Cuando la autorización se realiza context.Succeed correctamente, se invoca con el requisito satisfecho como su único parámetro.
Uso de un controlador para varios requisitos
A continuación se muestra un ejemplo de una relación uno a varios en la que un controlador de permisos puede controlar tres tipos diferentes de requisitos:
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using PoliciesAuthApp1.Services.Requirements;
public class PermissionHandler : IAuthorizationHandler
{
public Task HandleAsync(AuthorizationHandlerContext context)
{
var pendingRequirements = context.PendingRequirements.ToList();
foreach (var requirement in pendingRequirements)
{
if (requirement is ReadPermission)
{
if (IsOwner(context.User, context.Resource) ||
IsSponsor(context.User, context.Resource))
{
context.Succeed(requirement);
}
}
else if (requirement is EditPermission ||
requirement is DeletePermission)
{
if (IsOwner(context.User, context.Resource))
{
context.Succeed(requirement);
}
}
}
//TODO: Use the following if targeting a version of
//.NET Framework older than 4.6:
// return Task.FromResult(0);
return Task.CompletedTask;
}
private bool IsOwner(ClaimsPrincipal user, object resource)
{
// Code omitted for brevity
return true;
}
private bool IsSponsor(ClaimsPrincipal user, object resource)
{
// Code omitted for brevity
return true;
}
}
El código anterior recorre PendingRequirementsuna propiedad que contiene requisitos — no marcados como correctos. Para un requisito, el usuario debe ser propietario o patrocinador ReadPermission para acceder al recurso solicitado. En el caso de un requisito o , debe ser propietario EditPermission para acceder al recurso DeletePermission solicitado.
Registro del controlador
Los controladores se registran en la colección de servicios durante la configuración. Por ejemplo:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddRazorPages();
services.AddAuthorization(options =>
{
options.AddPolicy("AtLeast21", policy =>
policy.Requirements.Add(new MinimumAgeRequirement(21)));
});
services.AddSingleton<IAuthorizationHandler, MinimumAgeHandler>();
}
El código anterior se registra MinimumAgeHandler como singleton invocando services.AddSingleton<IAuthorizationHandler, MinimumAgeHandler>(); . Los controladores se pueden registrar mediante cualquiera de las duraciones de servicio integradas.
Es posible agrupar un requisito y un controlador en una sola clase que implementa IAuthorizationRequirement y IAuthorizationHandler . Esto crea un acoplamiento estricto entre el controlador y el requisito y solo se recomienda para los requisitos y controladores simples. La creación de una clase que implementa ambas interfaces elimina la necesidad de registrar el controlador en la di. Debido al PassThroughAuthorizationHandler integrado que permite que los requisitos se controlen por sí mismos.
Vea la clase AssertionRequirement para obtener un buen ejemplo en el que es un requisito y el controlador AssertionRequirement en una clase totalmente independiente.
¿Qué debe devolver un controlador?
Tenga en cuenta Handle que el método del ejemplo de controlador no devuelve ningún valor. ¿Cómo se indica un estado de éxito o error?
Un controlador indica que se ha realizado correctamente llamando a
context.Succeed(IAuthorizationRequirement requirement), pasando el requisito que se ha validado correctamente.Por lo general, un controlador no necesita controlar los errores, ya que otros controladores para el mismo requisito pueden tener éxito.
Para garantizar el error, incluso si otros controladores de requisitos se realiza correctamente, llame a
context.Fail.
Si un controlador llama a o , todavía se llama a context.Succeed context.Fail todos los demás controladores. Esto permite que los requisitos produzcan efectos secundarios, como el registro, que tiene lugar incluso si otro controlador ha validado correctamente o ha producido un error en un requisito. Cuando se establece en false , la propiedad InvokeHandlersAfterFailure cortocircuiza la ejecución de controladores cuando context.Fail se llama a . InvokeHandlersAfterFailure el valor predeterminado es true , en cuyo caso se llama a todos los controladores.
Nota
Se llama a los controladores de autorización incluso si se produce un error en la autenticación.
¿Por qué quiero varios controladores para un requisito?
En los casos en los que quiera que la evaluación se haga de forma or, implemente varios controladores para un único requisito. Por ejemplo, Microsoft tiene puertas que solo se abren con tarjetas de clave. Si deja la tarjeta de la clave en casa, la recepcionista imprime una etiqueta engomada temporal y abre la puerta automáticamente. En este escenario, tendría un único requisito, BuildingEntry, pero varios controladores, cada uno examinando un único requisito.
BuildingEntryRequirement.cs
using Microsoft.AspNetCore.Authorization;
public class BuildingEntryRequirement : IAuthorizationRequirement
{
}
BadgeEntryHandler.cs
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using PoliciesAuthApp1.Services.Requirements;
public class BadgeEntryHandler : AuthorizationHandler<BuildingEntryRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
BuildingEntryRequirement requirement)
{
if (context.User.HasClaim(c => c.Type == "BadgeId" &&
c.Issuer == "http://microsoftsecurity"))
{
context.Succeed(requirement);
}
//TODO: Use the following if targeting a version of
//.NET Framework older than 4.6:
// return Task.FromResult(0);
return Task.CompletedTask;
}
}
TemporaryStickerHandler.cs
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using PoliciesAuthApp1.Services.Requirements;
public class TemporaryStickerHandler : AuthorizationHandler<BuildingEntryRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
BuildingEntryRequirement requirement)
{
if (context.User.HasClaim(c => c.Type == "TemporaryBadgeId" &&
c.Issuer == "https://microsoftsecurity"))
{
// We'd also check the expiration date on the sticker.
context.Succeed(requirement);
}
//TODO: Use the following if targeting a version of
//.NET Framework older than 4.6:
// return Task.FromResult(0);
return Task.CompletedTask;
}
}
Asegúrese de que ambos controladores están registrados. Si alguno de los controladores se realiza correctamente cuando una directiva evalúa BuildingEntryRequirement , la evaluación de la directiva se realiza correctamente.
Uso de un func para cumplir una directiva
Puede haber situaciones en las que el cumplimiento de una directiva sea fácil de expresar en el código. Es posible proporcionar un al Func<AuthorizationHandlerContext, bool> configurar la directiva con el generador de RequireAssertion directivas.
Por ejemplo, el anterior BadgeEntryHandler podría reescribirse de la siguiente manera:
services.AddAuthorization(options =>
{
options.AddPolicy("BadgeEntry", policy =>
policy.RequireAssertion(context =>
context.User.HasClaim(c =>
(c.Type == "BadgeId" ||
c.Type == "TemporaryBadgeId") &&
c.Issuer == "https://microsoftsecurity")));
});
Acceso al contexto de solicitud de MVC en controladores
El HandleRequirementAsync método que implementa en un controlador de autorización tiene dos parámetros: y AuthorizationHandlerContext que está TRequirement controlando. Los marcos como MVC o pueden agregar cualquier objeto a SignalR la propiedad en para pasar información Resource AuthorizationHandlerContext adicional.
Cuando se usa el enrutamiento de punto de conexión, la autorización se controla normalmente mediante el middleware de autorización. En este caso, la Resource propiedad es una instancia de HttpContext . El contexto se puede usar para acceder al punto de conexión actual, que se puede usar para sondear el recurso subyacente al que está enrutando. Por ejemplo:
if (context.Resource is HttpContext httpContext)
{
var endpoint = httpContext.GetEndpoint();
var actionDescriptor = endpoint.Metadata.GetMetadata<ControllerActionDescriptor>();
...
}
Con el enrutamiento tradicional, o cuando la autorización se produce como parte del filtro de autorización de MVC, el valor Resource de es una instancia de AuthorizationFilterContext . Esta propiedad proporciona acceso a HttpContext , y a todo lo demás proporcionado por MVC y RouteData Razor Pages.
El uso de la Resource propiedad es específico del marco. El uso de la información Resource de la propiedad limita las directivas de autorización a marcos concretos. Debe convertir la propiedad mediante la palabra clave y, a continuación, confirmar que la conversión se ha ejecutado correctamente para asegurarse de que el código no se bloquea con cuando se ejecuta en Resource is otros InvalidCastException marcos:
// Requires the following import:
// using Microsoft.AspNetCore.Mvc.Filters;
if (context.Resource is AuthorizationFilterContext mvcContext)
{
// Examine MVC-specific things like routing data.
}
Exigir globalmente la autenticación de todos los usuarios
Para obtener información sobre cómo requerir globalmente la autenticación de todos los usuarios, vea Requerir usuarios autenticados.
En primer lugar, la autorización basada en roles y la autorización basada en notificaciones usan un requisito, un controlador de requisitos y una directiva preconfigurado. Estos bloques de creación admiten la expresión de evaluaciones de autorización en el código. El resultado es una estructura de autorización más enriquecible, reutilizable y que se puede probar.
Una directiva de autorización consta de uno o varios requisitos. Se registra como parte de la configuración del servicio de autorización, en el Startup.ConfigureServices método :
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddRazorPages();
services.AddAuthorization(options =>
{
options.AddPolicy("AtLeast21", policy =>
policy.Requirements.Add(new MinimumAgeRequirement(21)));
});
}
En el ejemplo anterior, se crea una directiva "AtLeast21". Tiene un único requisito — que tiene una antigüedad mínima, que se proporciona como parámetro para el requisito.
IAuthorizationService
El servicio principal que determina si la autorización es correcta es IAuthorizationService :
/// <summary>
/// Checks policy based permissions for a user
/// </summary>
public interface IAuthorizationService
{
/// <summary>
/// Checks if a user meets a specific set of requirements for the specified resource
/// </summary>
/// <param name="user">The user to evaluate the requirements against.</param>
/// <param name="resource">
/// An optional resource the policy should be checked with.
/// If a resource is not required for policy evaluation you may pass null as the value
/// </param>
/// <param name="requirements">The requirements to evaluate.</param>
/// <returns>
/// A flag indicating whether authorization has succeeded.
/// This value is <value>true</value> when the user fulfills the policy;
/// otherwise <value>false</value>.
/// </returns>
/// <remarks>
/// Resource is an optional parameter and may be null. Please ensure that you check
/// it is not null before acting upon it.
/// </remarks>
Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object resource,
IEnumerable<IAuthorizationRequirement> requirements);
/// <summary>
/// Checks if a user meets a specific authorization policy
/// </summary>
/// <param name="user">The user to check the policy against.</param>
/// <param name="resource">
/// An optional resource the policy should be checked with.
/// If a resource is not required for policy evaluation you may pass null as the value
/// </param>
/// <param name="policyName">The name of the policy to check against a specific
/// context.</param>
/// <returns>
/// A flag indicating whether authorization has succeeded.
/// Returns a flag indicating whether the user, and optional resource has fulfilled
/// the policy.
/// <value>true</value> when the policy has been fulfilled;
/// otherwise <value>false</value>.
/// </returns>
/// <remarks>
/// Resource is an optional parameter and may be null. Please ensure that you check
/// it is not null before acting upon it.
/// </remarks>
Task<AuthorizationResult> AuthorizeAsync(
ClaimsPrincipal user, object resource, string policyName);
}
El código anterior resalta los dos métodos de IAuthorizationService.
IAuthorizationRequirement es un servicio de marcador sin métodos y el mecanismo para realizar un seguimiento de si la autorización es correcta.
Cada IAuthorizationHandler uno de ellos es responsable de comprobar si se cumplen los requisitos:
/// <summary>
/// Classes implementing this interface are able to make a decision if authorization
/// is allowed.
/// </summary>
public interface IAuthorizationHandler
{
/// <summary>
/// Makes a decision if authorization is allowed.
/// </summary>
/// <param name="context">The authorization information.</param>
Task HandleAsync(AuthorizationHandlerContext context);
}
La AuthorizationHandlerContext clase es lo que usa el controlador para marcar si se han cumplido los requisitos:
context.Succeed(requirement)
El código siguiente muestra la implementación predeterminada simplificada (y anotada con comentarios) del servicio de autorización:
public async Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user,
object resource, IEnumerable<IAuthorizationRequirement> requirements)
{
// Create a tracking context from the authorization inputs.
var authContext = _contextFactory.CreateContext(requirements, user, resource);
// By default this returns an IEnumerable<IAuthorizationHandlers> from DI.
var handlers = await _handlers.GetHandlersAsync(authContext);
// Invoke all handlers.
foreach (var handler in handlers)
{
await handler.HandleAsync(authContext);
}
// Check the context, by default success is when all requirements have been met.
return _evaluator.Evaluate(authContext);
}
En el código siguiente se muestra un típico ConfigureServices :
public void ConfigureServices(IServiceCollection services)
{
// Add all of your handlers to DI.
services.AddSingleton<IAuthorizationHandler, MyHandler1>();
// MyHandler2, ...
services.AddSingleton<IAuthorizationHandler, MyHandlerN>();
// Configure your policies
services.AddAuthorization(options =>
options.AddPolicy("Something",
policy => policy.RequireClaim("Permission", "CanViewPage", "CanViewAnything")));
services.AddControllersWithViews();
services.AddRazorPages();
}
Use IAuthorizationService o para la [Authorize(Policy = "Something")] autorización.
Aplicación de directivas a controladores MVC
Si usa Pages, consulte Razor Aplicación de directivas a Razor páginas en este documento.
Las directivas se aplican a los controladores mediante el atributo [Authorize] con el nombre de directiva. Por ejemplo:
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
[Authorize(Policy = "AtLeast21")]
public class AlcoholPurchaseController : Controller
{
public IActionResult Index() => View();
}
Aplicación de directivas a Razor Pages
Las directivas se aplican Razor a Pages mediante el atributo con el nombre de [Authorize] directiva. Por ejemplo:
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;
[Authorize(Policy = "AtLeast21")]
public class AlcoholPurchaseModel : PageModel
{
}
Las directivas no se pueden aplicar en el nivel de controlador Razor de página, deben aplicarse a la página.
Las directivas se pueden aplicar a Razor Pages mediante una convención de autorización.
Requisitos
Un requisito de autorización es una colección de parámetros de datos que una directiva puede usar para evaluar la entidad de seguridad de usuario actual. En nuestra directiva "AtLeast21", el requisito es un único parámetro — de la antigüedad mínima. Un requisito implementa IAuthorizationRequirement,que es una interfaz de marcador vacía. Un requisito de antigüedad mínima parametrizada podría implementarse de la siguiente manera:
using Microsoft.AspNetCore.Authorization;
public class MinimumAgeRequirement : IAuthorizationRequirement
{
public int MinimumAge { get; }
public MinimumAgeRequirement(int minimumAge)
{
MinimumAge = minimumAge;
}
}
Si una directiva de autorización contiene varios requisitos de autorización, todos los requisitos deben pasar para que la evaluación de la directiva se haga correctamente. En otras palabras, varios requisitos de autorización agregados a una única directiva de autorización se tratan en función de AND.
Nota
No es necesario que un requisito tenga datos ni propiedades.
Controladores de autorización
Un controlador de autorización es responsable de la evaluación de las propiedades de un requisito. El controlador de autorización evalúa los requisitos con respecto a un AuthorizationHandlerContext proporcionado para determinar si se permite el acceso.
Un requisito puede tener varios controladores. Un controlador puede heredar AuthorizationHandler <TRequirement> , TRequirement donde es el requisito que se debe controlar. Como alternativa, un controlador puede implementar IAuthorizationHandler para controlar más de un tipo de requisito.
Usar un controlador para un requisito
A continuación se muestra un ejemplo de una relación uno a uno en la que un controlador de edad mínima utiliza un único requisito:
using System;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using PoliciesAuthApp1.Services.Requirements;
public class MinimumAgeHandler : AuthorizationHandler<MinimumAgeRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
MinimumAgeRequirement requirement)
{
if (!context.User.HasClaim(c => c.Type == ClaimTypes.DateOfBirth &&
c.Issuer == "http://contoso.com"))
{
//TODO: Use the following if targeting a version of
//.NET Framework older than 4.6:
// return Task.FromResult(0);
return Task.CompletedTask;
}
var dateOfBirth = Convert.ToDateTime(
context.User.FindFirst(c => c.Type == ClaimTypes.DateOfBirth &&
c.Issuer == "http://contoso.com").Value);
int calculatedAge = DateTime.Today.Year - dateOfBirth.Year;
if (dateOfBirth > DateTime.Today.AddYears(-calculatedAge))
{
calculatedAge--;
}
if (calculatedAge >= requirement.MinimumAge)
{
context.Succeed(requirement);
}
//TODO: Use the following if targeting a version of
//.NET Framework older than 4.6:
// return Task.FromResult(0);
return Task.CompletedTask;
}
}
El código anterior determina si la entidad de seguridad del usuario actual tiene una notificación de fecha de nacimiento emitida por un emisor conocido y de confianza. La autorización no puede producirse cuando falta la notificación, en cuyo caso se devuelve una tarea completada. Cuando hay una notificación, se calcula la edad del usuario. Si el usuario cumple la antigüedad mínima definida por el requisito, la autorización se considera correcta. Cuando la autorización se realiza context.Succeed correctamente, se invoca con el requisito satisfecho como su único parámetro.
Uso de un controlador para varios requisitos
A continuación se muestra un ejemplo de una relación uno a varios en la que un controlador de permisos puede controlar tres tipos diferentes de requisitos:
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using PoliciesAuthApp1.Services.Requirements;
public class PermissionHandler : IAuthorizationHandler
{
public Task HandleAsync(AuthorizationHandlerContext context)
{
var pendingRequirements = context.PendingRequirements.ToList();
foreach (var requirement in pendingRequirements)
{
if (requirement is ReadPermission)
{
if (IsOwner(context.User, context.Resource) ||
IsSponsor(context.User, context.Resource))
{
context.Succeed(requirement);
}
}
else if (requirement is EditPermission ||
requirement is DeletePermission)
{
if (IsOwner(context.User, context.Resource))
{
context.Succeed(requirement);
}
}
}
//TODO: Use the following if targeting a version of
//.NET Framework older than 4.6:
// return Task.FromResult(0);
return Task.CompletedTask;
}
private bool IsOwner(ClaimsPrincipal user, object resource)
{
// Code omitted for brevity
return true;
}
private bool IsSponsor(ClaimsPrincipal user, object resource)
{
// Code omitted for brevity
return true;
}
}
El código anterior recorre PendingRequirementsuna propiedad que contiene requisitos — no marcados como correctos. Para un requisito, el usuario debe ser propietario o patrocinador ReadPermission para acceder al recurso solicitado. En el caso de un requisito o , debe ser propietario EditPermission para acceder al recurso DeletePermission solicitado.
Registro del controlador
Los controladores se registran en la colección de servicios durante la configuración. Por ejemplo:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddRazorPages();
services.AddAuthorization(options =>
{
options.AddPolicy("AtLeast21", policy =>
policy.Requirements.Add(new MinimumAgeRequirement(21)));
});
services.AddSingleton<IAuthorizationHandler, MinimumAgeHandler>();
}
El código anterior se registra MinimumAgeHandler como singleton invocando services.AddSingleton<IAuthorizationHandler, MinimumAgeHandler>(); . Los controladores se pueden registrar mediante cualquiera de las duraciones de servicio integradas.
¿Qué debe devolver un controlador?
Tenga en cuenta Handle que el método del ejemplo de controlador no devuelve ningún valor. ¿Cómo se indica un estado de éxito o error?
Un controlador indica que se ha realizado correctamente llamando a
context.Succeed(IAuthorizationRequirement requirement), pasando el requisito que se ha validado correctamente.Por lo general, un controlador no necesita controlar los errores, ya que otros controladores para el mismo requisito pueden tener éxito.
Para garantizar el error, incluso si otros controladores de requisitos se realiza correctamente, llame a
context.Fail.
Si un controlador llama a o , todavía se llama a context.Succeed context.Fail todos los demás controladores. Esto permite que los requisitos produzcan efectos secundarios, como el registro, que tiene lugar incluso si otro controlador ha validado correctamente o ha producido un error en un requisito. Cuando se establece en false , la propiedad InvokeHandlersAfterFailure cortocircuiza la ejecución de controladores cuando context.Fail se llama a . InvokeHandlersAfterFailure el valor predeterminado es true , en cuyo caso se llama a todos los controladores.
Nota
Se llama a los controladores de autorización incluso si se produce un error en la autenticación.
¿Por qué quiero varios controladores para un requisito?
En los casos en los que quiera que la evaluación se haga de forma or, implemente varios controladores para un único requisito. Por ejemplo, Microsoft tiene puertas que solo se abren con tarjetas de clave. Si deja la tarjeta de la clave en casa, la recepcionista imprime una etiqueta engomada temporal y abre la puerta automáticamente. En este escenario, tendría un único requisito, BuildingEntry, pero varios controladores, cada uno examinando un único requisito.
BuildingEntryRequirement.cs
using Microsoft.AspNetCore.Authorization;
public class BuildingEntryRequirement : IAuthorizationRequirement
{
}
BadgeEntryHandler.cs
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using PoliciesAuthApp1.Services.Requirements;
public class BadgeEntryHandler : AuthorizationHandler<BuildingEntryRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
BuildingEntryRequirement requirement)
{
if (context.User.HasClaim(c => c.Type == "BadgeId" &&
c.Issuer == "http://microsoftsecurity"))
{
context.Succeed(requirement);
}
//TODO: Use the following if targeting a version of
//.NET Framework older than 4.6:
// return Task.FromResult(0);
return Task.CompletedTask;
}
}
TemporaryStickerHandler.cs
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using PoliciesAuthApp1.Services.Requirements;
public class TemporaryStickerHandler : AuthorizationHandler<BuildingEntryRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
BuildingEntryRequirement requirement)
{
if (context.User.HasClaim(c => c.Type == "TemporaryBadgeId" &&
c.Issuer == "https://microsoftsecurity"))
{
// We'd also check the expiration date on the sticker.
context.Succeed(requirement);
}
//TODO: Use the following if targeting a version of
//.NET Framework older than 4.6:
// return Task.FromResult(0);
return Task.CompletedTask;
}
}
Asegúrese de que ambos controladores están registrados. Si alguno de los controladores se realiza correctamente cuando una directiva evalúa BuildingEntryRequirement , la evaluación de la directiva se realiza correctamente.
Uso de un func para cumplir una directiva
Puede haber situaciones en las que el cumplimiento de una directiva sea fácil de expresar en el código. Es posible proporcionar un al Func<AuthorizationHandlerContext, bool> configurar la directiva con el generador de RequireAssertion directivas.
Por ejemplo, el anterior BadgeEntryHandler podría reescribirse de la siguiente manera:
services.AddAuthorization(options =>
{
options.AddPolicy("BadgeEntry", policy =>
policy.RequireAssertion(context =>
context.User.HasClaim(c =>
(c.Type == "BadgeId" ||
c.Type == "TemporaryBadgeId") &&
c.Issuer == "https://microsoftsecurity")));
});
Acceso al contexto de solicitud de MVC en controladores
El HandleRequirementAsync método que implementa en un controlador de autorización tiene dos parámetros: AuthorizationHandlerContext y que está TRequirement controlando. Los marcos como MVC o pueden agregar cualquier objeto a SignalR la propiedad en para pasar información Resource AuthorizationHandlerContext adicional.
Cuando se usa el enrutamiento de punto de conexión, la autorización se controla normalmente mediante el middleware de autorización. En este caso, la Resource propiedad es una instancia de Endpoint . El punto de conexión se puede usar para sondear el recurso subyacente al que está enrutando. Por ejemplo:
if (context.Resource is Endpoint endpoint)
{
var actionDescriptor = endpoint.Metadata.GetMetadata<ControllerActionDescriptor>();
...
}
El punto de conexión no proporciona acceso al HttpContext actual. Cuando se usa el enrutamiento de punto de conexión, use IHttpContextAcessor para acceder dentro de un controlador de HttpContext autorización. Para obtener más información, vea Usar HttpContext desde componentes personalizados.
Con el enrutamiento tradicional, o cuando la autorización se produce como parte del filtro de autorización de MVC, el valor Resource de es una instancia de AuthorizationFilterContext . Esta propiedad proporciona acceso a HttpContext , y a todo lo demás proporcionado por MVC y RouteData Razor Pages.
El uso de la Resource propiedad es específico del marco. El uso de la información Resource de la propiedad limita las directivas de autorización a marcos concretos. Debe convertir la propiedad mediante la palabra clave y, a continuación, confirmar que la conversión se ha ejecutado correctamente para asegurarse de que el código no se bloquea con cuando se ejecuta en Resource is otros InvalidCastException marcos:
// Requires the following import:
// using Microsoft.AspNetCore.Mvc.Filters;
if (context.Resource is AuthorizationFilterContext mvcContext)
{
// Examine MVC-specific things like routing data.
}
Exigir globalmente la autenticación de todos los usuarios
Para obtener información sobre cómo requerir globalmente la autenticación de todos los usuarios, vea Requerir usuarios autenticados.