Compartilhar via


Autorização baseada em declaração no ASP.NET Core

Quando uma identidade é criada para um usuário de aplicativo ao entrar em um aplicativo, o provedor de identidade pode atribuir uma ou mais declarações à identidade do usuário. Uma declaração é um par de valores nome-valor que representa o que o assunto (um usuário, um aplicativo ou serviço, ou um dispositivo/computador) é, e não o que o assunto pode fazer. Uma declaração pode ser avaliada pelo aplicativo para determinar os direitos de acesso a dados e outros recursos protegidos durante o processo de autorização e também pode ser usada para tomar ou expressar decisões de autenticação sobre um assunto. Uma identidade pode conter várias declarações com vários valores e pode conter várias declarações do mesmo tipo. Este artigo explica como adicionar verificações de declarações para autorização em um aplicativo ASP.NET Core.

Este artigo usa exemplos Razor de componente e se concentra em Blazor cenários de autorização. Para obter diretrizes adicionais Blazor , consulte a seção Recursos adicionais . Para obter diretrizes sobre Razor Pages e MVC, consulte os seguintes recursos:

  • Autorização baseada em reivindicação nas Páginas do ASP.NET Core
  • autorização baseada em declarações no ASP.NET Core MVC

Aplicativo de exemplo

O exemplo Blazor Web App deste artigo é o aplicativo de exemplo BlazorWebAppAuthorization (repositório GitHub dotnet/AspNetCore.Docs.Samples) (como baixar). O aplicativo de exemplo usa contas semeadas com declarações pré-configuradas para demonstrar a maioria dos exemplos neste artigo. Para obter mais informações, consulte o arquivo README do exemplo (README.md).

Cuidado

Este aplicativo de exemplo usa um banco de dados na memória para armazenar informações do usuário, o que não é adequado para cenários de produção. O aplicativo de exemplo destina-se apenas a fins de demonstração e não deve ser usado como ponto de partida para aplicativos de produção.

Adicionar verificações de declaração

Verificações de autorização baseadas em declaração:

O AuthorizeView componente (AuthorizeView componente na Blazor documentação) dá suporte à autorização baseada em política, em que a política requer uma ou mais declarações de direitos. Como alternativa, uma autorização baseada em declarações por meio de uma ou mais verificações de política pode ser configurada usando [Authorize] atributos em Razor componentes. O desenvolvedor deve criar e registrar uma política expressando os requisitos de declarações. Esta seção aborda conceitos básicos. Para obter cobertura completa, consulte ASP.NET Core Blazor autenticação e autorização.

O tipo mais simples de política de reivindicação procura a presença de uma reivindicação e não verifica o valor.

O registro da política ocorre como parte da configuração do serviço de autorização no arquivo do Program aplicativo:

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

Observação

WebApplicationBuilder.ConfigureApplication (reference source) adiciona automaticamente uma chamada para UseAuthorization quando IAuthorizationHandlerProvider é registrado, o que tem sido o comportamento do ASP.NET Core desde o lançamento do .NET 8. Portanto, chamar UseAuthorization explicitamente para aplicativos Blazor do lado do servidor no .NET 8 ou posterior é tecnicamente redundante, mas a chamada não é prejudicial. Chamando-o no código do desenvolvedor depois que ele já tiver sido chamado pela estrutura não tem efeito.

Observação

Links de documentação para .NET fonte de referência geralmente carregam o branch padrão do repositório, que representa o desenvolvimento atual para a próxima versão do .NET. Para selecionar uma marca para uma versão específica, use a lista suspensa para Alternar branches ou marcas. Para obter mais informações, consulte Como selecionar uma marca de versão do código-fonte ASP.NET Core (dotnet/AspNetCore.Docs #26205).

O registro da política ocorre como parte da configuração do serviço de autorização no arquivo do Program aplicativo:

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

Em aplicativos Blazor Server, chame UseAuthorization após a linha que chama UseAuthentication (se estiver presente):

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

O registro da política ocorre como parte da configuração do serviço de autorização em Startup.ConfigureServices (Startup.cs):

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

Em Blazor Server aplicativos, chame UseAuthorization em Startup.Configure após a linha que chama UseAuthentication (se presente):

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

Blazor WebAssembly aplicativos chamam AddAuthorizationCore no arquivo Program para adicionar serviços de autorização:

builder.Services.AddAuthorizationCore();

Aplique a política usando a Policy propriedade no [Authorize] atributo para especificar o nome da política. No exemplo a seguir, a EmployeeOnly política verifica a presença de uma declaração EmployeeNumber na identidade atual:

Para autorização baseada em política com um componente AuthorizeView, use o parâmetro AuthorizeView.Policy com um único nome de política.

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 a política usando a Policy propriedade no [Authorize] atributo para especificar o nome da política. No exemplo a seguir, a EmployeeOnly política verifica a presença de uma declaração EmployeeNumber na identidade atual:

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>

Você pode especificar uma lista de valores permitidos ao criar uma política. A política a seguir só passa para funcionários cujo número de funcionário seja 1, 2, 3, 4 ou 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>

Adicionar uma verificação de declaração genérica

Se o valor da declaração não for um único valor ou você precisar de uma lógica de avaliação de declaração mais flexível, como correspondência de padrões, verificação do emissor da declaração ou análise de valores de declaração complexos, use RequireAssertion com HasClaim. Por exemplo, a política a seguir exige que a declaração do email usuário termine com um domínio 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 obter mais informações, consulte Autorização baseada emPolicy em ASP.NET Core.

Avaliar várias políticas

Várias políticas são aplicadas por meio de vários AuthorizeView componentes. O componente interno exige que o usuário passe sua política e todas as políticas dos componentes pai AuthorizeView.

O exemplo a seguir:

  • Requer uma CustomerServiceMember diretriz, que indica que o usuário está no departamento de atendimento ao cliente da organização, dado que possui uma Department reivindicação com um valor de Customer Service.
  • Também requer uma política HumanResourcesMember, que indica que o usuário está no departamento de recursos humanos da organização porque possui uma declaração Department com um valor de Human Resources.

No arquivo Program do aplicativo:

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

No arquivo Program do aplicativo:

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

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

O exemplo a seguir usa 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>

O exemplo a seguir usa atributos[Authorize].

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 políticas mais complicadas, como interpretar uma declaração de data de nascimento, calcular uma idade a partir dela e verificar se a idade é 21 anos ou mais, utilize RequireAssertion ou escreva manipuladores de política personalizados. Manipuladores de política personalizados são úteis quando você precisa de acesso a serviços injetados por dependência ou deseja um componente de autorização reutilizável e testável.

Sensibilidade a maiúsculas e minúsculas

Os valores de reivindicação são comparados usando StringComparison.Ordinal. Isso significa que Admin (maiúscula A) e admin (minúscula a) são sempre considerados papéis diferentes, independentemente de qual manipulador de autenticação criou a identidade.

Separadamente, a comparação de tipo de reivindicação (usada para localizar declarações de função por seu tipo de reivindicação, como http://schemas.microsoft.com/ws/2008/06/identity/claims/role) pode diferenciar entre sensível ou insensível a maiúsculas, dependendo da ClaimsIdentity implementação. Com Microsoft.IdentityModel em ASP.NET Core 8.0 ou posterior (usado por AddJwtBearer, AddOpenIdConnect, AddWsFederation e AddMicrosoftIdentityWebApp/AddMicrosoftIdentityWebApi), CaseSensitiveClaimsIdentity é produzido durante a validação de tokens, que usa correspondência exata de tipos de declaração sensível a maiúsculas e minúsculas.

O ClaimsIdentity padrão fornecido pelo .NET runtime (usado na maioria dos casos, incluindo todos os fluxos baseados em cookie) ainda utiliza a correspondência de tipos de declaração que não diferencia maiúsculas de minúsculas.

Na prática, essa distinção raramente importa para autorização de função porque o tipo de declaração de função é definido uma vez durante a criação de identidade e correspondido consistentemente. Sempre use maiúsculas e minúsculas consistentes para nomes de funções e tipos de reivindicação para evitar problemas sutis.

Recursos adicionais