Condividi tramite


Autorizzazione basata su attestazioni in ASP.NET Core

Quando viene creata un'identità per un utente dell'app al momento dell'accesso a un'app, il provider di identità può assegnare una o più attestazioni all'identità dell'utente. Un'attestazione è una coppia di valori del nome che rappresenta l'oggetto (un utente, un'app o un servizio o un dispositivo/computer) e non ciò che può fare l'oggetto. Un'attestazione può essere valutata dall'app per determinare i diritti di accesso ai dati e altre risorse protette durante il processo di autorizzazione e può essere usata anche per prendere o esprimere decisioni di autenticazione su un oggetto. Un'identità può contenere più attestazioni con più valori e può contenere più attestazioni dello stesso tipo. Questo articolo illustra come aggiungere verifiche delle attestazioni per l'autorizzazione in un'app ASP.NET Core.

Questo articolo usa Razor esempi di componenti e si concentra sugli Blazor scenari di autorizzazione. Per altre indicazioni, vedere la sezione Blazor. Per le linee guida su Razor Pages e MVC, vedere le risorse seguenti:

  • Autorizzazione Basata su Dichiarazione in ASP.NET Core Pages
  • Autorizzazione basata su dichiarazioni in ASP.NET Core MVC

Esempio di app

L'esempio Blazor Web App per questo articolo è l'app di esempio BlazorWebAppAuthorization (repository dotnet/AspNetCore.Docs.Samples GitHub) (come scaricare). L'app di esempio utilizza account preconfigurati con affermazioni preconfigurate per illustrare la maggior parte degli esempi in questo articolo. Per altre informazioni, vedere il file README dell'esempio (README.md).

Attenzione

Questa app di esempio usa un database in memoria per archiviare le informazioni utente, che non sono adatte per gli scenari di produzione. L'app di esempio è destinata solo a scopi dimostrativi e non deve essere usata come punto di partenza per le app di produzione.

Aggiungere controlli delle rivendicazioni

Controlli di autorizzazione basati su attestazioni:

Il AuthorizeView componente (AuthorizeView componente nella Blazor documentazione) supporta l'autorizzazione basata su criteri, in cui il criterio richiede una o più attestazioni. In alternativa, è possibile configurare un'autorizzazione basata su attestazioni tramite uno o più controlli dei criteri usando [Authorize] gli attributi nei Razor componenti. Lo sviluppatore deve creare e registrare una politica che definisce i requisiti delle attestazioni. Questa sezione illustra i concetti di base. Per una copertura completa, vedere ASP.NET Core Blazor autenticazione e autorizzazione.

Il tipo più semplice di politica di reclamo cerca la presenza di un reclamo e non controlla il valore.

La registrazione dei criteri viene eseguita come parte della configurazione del servizio di autorizzazione nel file dell'app Program :

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

Annotazioni

WebApplicationBuilder.ConfigureApplication (reference source) aggiunge automaticamente una chiamata per UseAuthorization quando viene registrato IAuthorizationHandlerProvider, che è stato il comportamento per ASP.NET Core dal rilascio di .NET 8. Pertanto, chiamare UseAuthorization in modo esplicito per le app sul lato server Blazor in .NET 8 o versione successiva è tecnicamente ridondante, ma la chiamata non è dannosa. Chiamandolo nel codice per sviluppatori dopo che è già stato chiamato dal framework semplicemente no-ops.

Annotazioni

I collegamenti alla documentazione di riferimento .NET in genere caricano il ramo predefinito del repository, che rappresenta lo sviluppo corrente per la versione successiva di .NET. Per selezionare un tag per una versione specifica, utilizzare il menu a tendina Seleziona rami o tag. Per altre informazioni, vedere Come selezionare un tag di versione del codice sorgente ASP.NET Core (dotnet/AspNetCore.Docs #26205).

La registrazione dei criteri viene eseguita come parte della configurazione del servizio di autorizzazione nel file dell'app Program :

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

Nelle Blazor Server app chiamare UseAuthorization dopo la riga che chiama UseAuthentication (se presente):

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

La registrazione dei criteri avviene come parte della configurazione del servizio di autorizzazione in Startup.ConfigureServices (Startup.cs):

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

Nelle Blazor Server app chiamare UseAuthorization dopo Startup.Configure la riga che chiama UseAuthentication (se presente):

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

Blazor WebAssembly le app chiamano AddAuthorizationCore nel Program file per aggiungere i servizi di autorizzazione:

builder.Services.AddAuthorizationCore();

Applicare il criterio utilizzando la proprietà Policy sull'attributo [Authorize] per specificare il nome del criterio. Nell'esempio seguente, la policy controlla EmployeeOnly la presenza di un'attestazione EmployeeNumber nell'identità corrente:

Per l'autorizzazione basata su criteri usando un AuthorizeView componente, usare il AuthorizeView.Policy parametro con un singolo nome di criteri.

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>

In alternativa, applicare il criterio usando la proprietà Policy sull' attributo[Authorize] per specificare il nome del criterio. Nell'esempio seguente, la policy controlla EmployeeOnly la presenza di un'attestazione EmployeeNumber nell'identità corrente:

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>

È possibile specificare un elenco di valori consentiti durante la creazione di un criterio. Il criterio seguente passa solo per i dipendenti il cui numero di dipendente è 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>

Aggiungere un controllo attestazione generico

Se il valore dell'attestazione non è un singolo valore o è necessaria una logica di valutazione dell'attestazione più flessibile, ad esempio criteri di corrispondenza, controllo dell'autorità emittente dell'attestazione o analisi di valori di attestazioni complessi, usare RequireAssertion con HasClaim. Ad esempio, il criterio seguente richiede che l'attestazione dell'utente email termini con un dominio specifico:

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))));
});

Per altre informazioni, vedere l'autorizzazione basata su criteri in ASP.NET Core.

Valutare più criteri

Più criteri vengono applicati tramite più AuthorizeView componenti. Il componente interno richiede all'utente di soddisfare i suoi criteri e tutti i criteri dei componenti padre AuthorizeView.

L'esempio seguente:

  • Richiede una CustomerServiceMember politica, che indica che l'utente si trova nel reparto del servizio clienti dell'organizzazione perché ha una richiesta Department con un valore di Customer Service.
  • È inoltre necessario un criterio HumanResourcesMember, che indica che l'utente si trova nel reparto risorse umane dell'organizzazione perché ha un'attestazione Department con un valore di Human Resources.

Nel file dell'app Program :

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

Nel file dell'app Program :

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

In 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"));
});

Nell'esempio seguente vengono utilizzati i componenti AuthorizeView.

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>

Nell'esempio seguente vengono utilizzati [Authorize] attributi.

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>

Per criteri più complessi, ad esempio prendere una data di nascita, calcolarne l'età e controllare che l'età sia maggiore o uguale a 21 anni, usare RequireAssertion o scrivere gestori di criteri personalizzati. I gestori di criteri personalizzati sono utili quando è necessario accedere ai servizi iniettati tramite dependency injection o se si desidera un componente di autorizzazione riutilizzabile e testabile.

Sensibilità alle maiuscole e minuscole delle richieste

I valori delle attestazioni vengono confrontati usando StringComparison.Ordinal. Ciò significa che Admin (maiuscolo A) e admin (minuscolo a) sono sempre considerati ruoli distinti, indipendentemente dal quale gestore di autenticazione ha creato l'identità.

Separatamente, il confronto del tipo di attestazione (usato per individuare le attestazioni di ruolo in base al tipo di attestazione, ad esempio http://schemas.microsoft.com/ws/2008/06/identity/claims/role) può distinguere tra maiuscole e minuscole o essere insensibile a tale distinzione a seconda dell'implementazione ClaimsIdentity. Con Microsoft.IdentityModel in ASP.NET Core 8.0 o versione successiva (usato da AddJwtBearer, AddOpenIdConnect, AddWsFederation e AddMicrosoftIdentityWebApp/AddMicrosoftIdentityWebApi), CaseSensitiveClaimsIdentity viene generato durante la convalida del token, che usa la corrispondenza del tipo di attestazione con distinzione tra maiuscole e minuscole.

Il valore predefinito ClaimsIdentity fornito dal runtime di .NET (usato nella maggior parte dei casi, inclusi tutti i flussi basati su cookie) usa comunque la corrispondenza del tipo di attestazione senza distinzione tra maiuscole e minuscole.

In pratica, questa distinzione è raramente importante per l'autorizzazione del ruolo perché il tipo di attestazione del ruolo viene impostato una sola volta durante la creazione dell'identità e corrisponde in modo coerente. Usare sempre una formattazione coerente di maiuscole e minuscole per i nomi dei ruoli e i tipi di claims per evitare problemi difficili da rilevare.

Risorse aggiuntive