Autorización basada en reclamaciones en ASP.NET Core

Cuando se crea una identidad para un usuario de aplicación al iniciar sesión en una aplicación, el proveedor de identidades puede asignar una o varias notificaciones a la identidad del usuario. Una declaración es un par nombre-valor que representa lo que el sujeto (un usuario, una aplicación o servicio, o un dispositivo o equipo) es, no lo que el sujeto puede hacer. La aplicación puede evaluar una notificación para determinar los derechos de acceso a los datos y otros recursos protegidos durante el proceso de autorización y también se puede usar para tomar o expresar decisiones de autenticación sobre un sujeto. Una identidad puede contener múltiples declaraciones con múltiples valores y puede contener múltiples declaraciones del mismo tipo. En este artículo se explica cómo agregar verificaciones de declaraciones para la autorización en una aplicación de ASP.NET Core.

En este artículo se usan Razor ejemplos de componentes y se centra en Blazor escenarios de autorización. Para obtener instrucciones adicionales Blazor , consulte la sección Contenido relacionado . Para obtener instrucciones de Razor Pages y MVC, consulte los siguientes recursos:

Aplicación de ejemplo

El ejemplo de Blazor Web App para este artículo es la aplicación de ejemplo BlazorWebAppAuthorization (dotnet/AspNetCore.Docs.Samples GitHub repository) (how to download). La aplicación de ejemplo utiliza cuentas inicializadas con reclamaciones preconfiguradas para ilustrar la mayoría de los ejemplos de este artículo. Para obtener más información, consulte el archivo LÉAME del ejemplo (README.md).

Precaución

Esta aplicación de ejemplo usa una base de datos en memoria para almacenar información de usuario, que no es adecuada para escenarios de producción. La aplicación de ejemplo está pensada solo para fines de demostración y no debe usarse como punto de partida para las aplicaciones de producción.

Añadir comprobaciones de reclamaciones

Comprobaciones de autorización basadas en reclamaciones

El componente de AuthorizeView (componente de AuthorizeView en la documentación de Blazor) admite la autorización basada en directivas, donde la directiva requiere una o varias reclamaciones. Como alternativa, se puede configurar una autorización basada en declaraciones mediante una o más comprobaciones de políticas usando atributos [Authorize] en componentes Razor. El desarrollador debe compilar y registrar una directiva que exprese los requisitos de notificaciones. En esta sección se tratan los conceptos básicos. Para obtener cobertura completa, consulte ASP.NET Core Blazor autenticación y autorización.

El tipo más sencillo de política de reclamaciones busca la presencia de una reclamación y no verifica su valor.

El registro de la directiva tiene lugar como parte de la configuración del servicio de autorización en el archivo de Program la aplicación:

builder.Services.AddAuthorizationBuilder()
    .AddPolicy("EmployeeOnly", policy => policy.RequireClaim("EmployeeNumber"));

Nota:

WebApplicationBuilder.ConfigureApplication (reference source) agrega automáticamente una llamada a UseAuthorization cuando se registra IAuthorizationHandlerProvider, que ha sido el comportamiento de ASP.NET Core desde la publicación de .NET 8. Por lo tanto, llamar a UseAuthorization explícitamente para aplicaciones de Blazor del lado servidor en .NET 8 o posterior es técnicamente redundante, pero la llamada no es perjudicial. Si se llama en el código del desarrollador después de que el marco ya lo haya llamado, básicamente no tiene ningún efecto.

Nota:

Los vínculos de la documentación al origen de referencia de .NET cargan normalmente la rama predeterminada del repositorio, que representa el desarrollo actual para la próxima versión de .NET. Para seleccionar una etiqueta para una versión específica, use la lista desplegable Cambiar ramas o etiquetas .

El registro de la directiva tiene lugar como parte de la configuración del servicio de autorización en el archivo de Program la aplicación:

builder.Services.AddAuthorizationBuilder()
    .AddPolicy("EmployeeOnly", policy => policy.RequireClaim("EmployeeNumber"));

En las aplicaciones de Blazor Server, llame a UseAuthorization después de la línea que llama a UseAuthentication (si está presente):

app.UseAuthentication(); // Only present if not called internally
app.UseAuthorization();

El registro de la directiva tiene lugar como parte de la configuración del servicio de autorización en Startup.ConfigureServices (Startup.cs):

services.AddAuthorization(options =>
{
    options.AddPolicy("EmployeeOnly", policy =>
        policy.RequireClaim("EmployeeNumber"));
});

En las aplicaciones de Blazor Server, llame a UseAuthorization en Startup.Configure después de la línea que llama a UseAuthentication (si está presente):

app.UseAuthentication(); // Only present if not called internally
app.UseAuthorization();

Las aplicaciones de Blazor WebAssembly llaman a AddAuthorizationCore en el archivo Program para agregar servicios de autorización:

builder.Services.AddAuthorizationCore();

Aplique la directiva mediante la Policy propiedad en el [Authorize] atributo para especificar el nombre de la directiva. En el ejemplo siguiente, la directiva EmployeeOnly comprueba la presencia de una reclamación de EmployeeNumber en la identidad actual.

Para la autorización basada en directivas mediante un AuthorizeView componente, use el AuthorizeView.Policy parámetro con un nombre de directiva único.

Pages/PassEmployeeOnlyPolicyWithAuthorizeView.razor:

@page "/pass-employeeonly-policy-with-authorizeview"

<h1>Pass 'EmployeeOnly' policy with AuthorizeView</h1>

<AuthorizeView Policy="EmployeeOnly">
    <Authorized>
        <p>You satisfy the 'EmployeeOnly' policy.</p>
    </Authorized>
    <NotAuthorized>
        <p>You <b>don't</b> satisfy the 'EmployeeOnly' policy.</p>
    </NotAuthorized>
</AuthorizeView>

Como alternativa, aplique la directiva mediante la Policy propiedad en el [Authorize] atributo para especificar el nombre de la directiva. En el ejemplo siguiente, la directiva EmployeeOnly comprueba la presencia de una reclamación de EmployeeNumber en la identidad actual.

Pages/PassEmployeeOnlyPolicyWithAuthorizeAttribute.razor:

@page "/pass-employeeonly-policy-with-authorize-attribute"
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize(Policy = "EmployeeOnly")]

<h1>Pass 'EmployeeOnly' policy with [Authorize] attribute</h1>

<p>You satisfy the 'EmployeeOnly' policy.</p>

Puede especificar una lista de valores permitidos al crear una directiva. La siguiente directiva aplica solo a los empleados cuyo número de empleado es 1, 2, 3, 4 o 5:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();

builder.Services.AddAuthorizationBuilder()
    .AddPolicy("Founders", policy =>
        policy.RequireClaim("EmployeeNumber", "1", "2", "3", "4", "5"));

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseAuthentication();
app.UseAuthorization();

app.MapDefaultControllerRoute();
app.MapRazorPages();

app.Run();
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("Founders", policy =>
                      policy.RequireClaim("EmployeeNumber", "1", "2", "3", "4", "5"));
});

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseAuthentication();
app.UseAuthorization();

app.MapDefaultControllerRoute();
app.MapRazorPages();

app.Run();
services.AddAuthorization(options =>
{
    options.AddPolicy("Founder", policy =>
        policy.RequireClaim("EmployeeNumber", "1", "2", "3", "4", "5"));
});

Pages/PassFounderPolicyWithAuthorizeView.razor:

@page "/pass-founder-policy-with-authorizeview"

<h1>Pass 'Founder' policy with AuthorizeView</h1>

<AuthorizeView Policy="Founder">
    <Authorized>
        <p>You satisfy the 'Founder' policy.</p>
    </Authorized>
    <NotAuthorized>
        <p>You <b>don't</b> satisfy the 'Founder' policy.</p>
    </NotAuthorized>
</AuthorizeView>

Agregar una comprobación de notificación genérica

Si el valor de la notificación no es un valor único o necesita una lógica de evaluación de notificaciones más flexible, como la coincidencia de patrones, la comprobación del emisor de notificaciones o el análisis de valores de notificación complejos, use RequireAssertion con HasClaim. Por ejemplo, la siguiente política requiere que la declaración del email usuario termine con un dominio específico.

builder.Services.AddAuthorizationBuilder()
    .AddPolicy("ContosoOnly", policy =>
        policy.RequireAssertion(context =>
            context.User.HasClaim(c =>
                c.Type == "email" &&
                c.Value.EndsWith("@contoso.com", StringComparison.OrdinalIgnoreCase))));
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("ContosoOnly", policy =>
        policy.RequireAssertion(context =>
            context.User.HasClaim(c =>
                c.Type == "email" &&
                c.Value.EndsWith("@contoso.com", StringComparison.OrdinalIgnoreCase))));
});
services.AddAuthorization(options =>
{
    options.AddPolicy("ContosoOnly", policy =>
        policy.RequireAssertion(context =>
            context.User.HasClaim(c =>
                c.Type == "email" &&
                c.Value.EndsWith("@contoso.com", StringComparison.OrdinalIgnoreCase))));
});

Para obtener más información, vea Autorización basada en directivas en ASP.NET Core.

Evaluación de varias directivas

Se aplican varias directivas a través de varios AuthorizeView componentes. El componente interno requiere que el usuario pase su directiva y cada directiva de componentes primarios de AuthorizeView.

En el ejemplo siguiente:

  • Requiere una directiva CustomerServiceMember, que indica que el usuario está en el departamento de atención al cliente de la organización porque tiene una reclamación de Department con un valor de Customer Service.
  • También requiere una directiva HumanResourcesMember, que indica que el usuario está en el departamento de recursos humanos de la organización porque tiene una reclamación de Department con un valor de Human Resources.

En el archivo Program de la aplicación:

builder.Services.AddAuthorizationBuilder()
    .AddPolicy("CustomerServiceMember", policy =>
        policy.RequireClaim("Department", "Customer Service"))
    .AddPolicy("HumanResourcesMember", policy =>
        policy.RequireClaim("Department", "Human Resources"));

En el archivo Program de la aplicación:

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("CustomerServiceMember", policy =>
        policy.RequireClaim("Department", "Customer Service"));
    options.AddPolicy("HumanResourcesMember", policy =>
        policy.RequireClaim("Department", "Human Resources"));
});

En Startup.ConfigureServices (Startup.cs):

services.AddAuthorization(options =>
{
    options.AddPolicy("CustomerServiceMember", policy =>
        policy.RequireClaim("Department", "Customer Service"));
    options.AddPolicy("HumanResourcesMember", policy =>
        policy.RequireClaim("Department", "Human Resources"));
});

En el ejemplo siguiente se usan AuthorizeView componentes.

Pages/PassCustomerServiceMemberAndHumanResourcesMemberPoliciesWithAuthorizeViews.razor:

@page "/pass-customerservicemember-and-humanresourcesmember-policies-with-authorizeviews"

<h1>Pass 'CustomerServiceMember' and 'HumanResourcesMember' policies with AuthorizeViews</h1>

<AuthorizeView Policy="CustomerServiceMember">
    <Authorized>
        <p>User: @context.User.Identity?.Name</p>
        <AuthorizeView Policy="HumanResourcesMember" Context="innerContext">
            <Authorized>
                <p>
                    You satisfy the 'CustomerServiceMember' and 'HumanResourcesMember' policies.
                </p>
            </Authorized>
            <NotAuthorized>
                <p>
                    You satisfy the 'CustomerServiceMember' policy, but you <b>don't</b> satisfy 
                    the 'HumanResourcesMember' policy.
                </p>
            </NotAuthorized>
        </AuthorizeView>
    </Authorized>
    <NotAuthorized>
        <p>
            You <b>don't</b> satisfy the 'CustomerServiceMember' policy.
        </p>
    </NotAuthorized>
</AuthorizeView>

En el ejemplo siguiente se usan [Authorize] atributos.

Pages/PassCustomerServiceMemberAndHumanResourcesMemberPoliciesWithAuthorizeAttributes.razor:

@page "/pass-customerservicemember-and-humanresourcesmember-policies-with-authorize-attributes"
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize(Policy = "CustomerServiceMember")]
@attribute [Authorize(Policy = "HumanResourcesMember")]

<h1>
    Pass 'CustomerServiceMember' and 'HumanResourcesMember' policies with [Authorize] attributes
</h1>

<p>
    You satisfy the 'CustomerServiceMember' and 'HumanResourcesMember' policies.
</p>

Para directivas más complicadas, como tomar una fecha de nacimiento, calcular una edad a partir de ella, comprobar que la edad es de 21 o más, usar RequireAssertion o escribir controladores de directivas personalizados. Los controladores de políticas personalizados son útiles cuando se necesita acceso a servicios inyectados por dependencias o si desea un componente de autorización reutilizable y verificable.

Distinción entre mayúsculas y minúsculas en las reclamaciones

Los valores de reclamación se comparan mediante StringComparison.Ordinal. Esto significa que Admin (en mayúsculas A) y admin (en minúsculas a) siempre se consideran roles diferentes, independientemente del controlador de autenticación que creó la identidad.

Por otra parte, la comparación del tipo de reclamación (que se utiliza para localizar reclamaciones de rol por su tipo, como http://schemas.microsoft.com/ws/2008/06/identity/claims/role) puede distinguir o no entre mayúsculas y minúsculas, dependiendo de la implementación de ClaimsIdentity. Con Microsoft.IdentityModel en ASP.NET Core 8.0 o versión posterior (que usan AddJwtBearer, AddOpenIdConnect, AddWsFederation y AddMicrosoftIdentityWebApp/AddMicrosoftIdentityWebApi), CaseSensitiveClaimsIdentity se genera durante la validación de tókenes, que utiliza una coincidencia de tipos de reclamación que distingue entre mayúsculas y minúsculas.

La ClaimsIdentity predeterminada proporcionada por el entorno de ejecución de .NET (que se usa en la mayoría de los casos, incluidos todos los flujos basados en cookie) sigue utilizando coincidencias de tipos de reclamación que no distinguen entre mayúsculas y minúsculas.

En la práctica, esta distinción rara vez tiene importancia para la autorización basada en roles, ya que el tipo de reclamación de rol se establece una sola vez durante la creación de la identidad y se aplica de forma coherente. Utilice siempre las mismas mayúsculas y minúsculas en los nombres de los roles y los tipos de reclamaciones para evitar problemas sutiles.