Partilhar via


Autorização baseada em reivindicações no ASP.NET Core

Quando uma identidade é criada para um utilizador da aplicação ao iniciar sessão numa aplicação, o fornecedor de identidade pode atribuir uma ou mais reivindicações à identidade do utilizador. Uma reivindicação é um par nome-valor que representa o que o sujeito (um utilizador, uma aplicação ou serviço, ou um dispositivo/computador) é, não o que o sujeito pode fazer. Uma reivindicação pode ser avaliada pela aplicação para determinar direitos de acesso a dados e outros recursos seguros durante o processo de autorização e pode também ser usada para tomar ou expressar decisões de autenticação sobre um sujeito. Uma identidade pode conter múltiplas reivindicações com múltiplos valores e pode conter múltiplas reivindicações do mesmo tipo. Este artigo explica como adicionar verificações de sinistros para autorização numa aplicação ASP.NET Core.

Este artigo utiliza Razor exemplos de componentes e foca-se em Blazor cenários de autorização. Para orientações adicionais Blazor , consulte a secção de Recursos Adicionais . Para obter orientações sobre Razor Pages e MVC, consulte os seguintes recursos:

Aplicativo de exemplo

O exemplo Blazor Web App deste artigo é a aplicação de exemplo BlazorWebAppAuthorization (repositório dotnet/AspNetCore.Docs.Samples GitHub) (como descarregar). A aplicação de exemplo utiliza contas pré-definidas com declarações pré-configuradas para demonstrar a maioria dos exemplos deste artigo. Para mais informações, consulte o ficheiro README do exemplo (README.md).

Atenção

Esta aplicação de exemplo utiliza uma base de dados em memória para armazenar informações do utilizador, o que não é adequado para cenários de produção. A aplicação de exemplo destina-se apenas a fins de demonstração e não deve ser usada como ponto de partida para aplicações de produção.

Adicionar verificações de sinistros

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

O AuthorizeView componente (AuthorizeView componente na Blazor documentação) suporta a autorização baseada em políticas , quando a apólice exige uma ou mais reclamações. Alternativamente, uma autorização baseada em declarações através de uma ou mais verificações de política pode ser configurada usando [Authorize] atributos nos Razor componentes. O promotor deve criar e registar uma apólice que expresse os requisitos de reclamação. Esta secção cobre conceitos básicos. Para cobertura completa, consulte ASP.NET Core Blazor autenticação e autorização.

O tipo mais simples de apólice de sinistro procura a presença de uma reclamação e não verifica o valor.

O registo da política ocorre como parte da configuração do serviço de autorização no ficheiro da Program aplicação:

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

Observação

WebApplicationBuilder.ConfigureApplication (reference source) adiciona automaticamente uma chamada para UseAuthorization quando IAuthorizationHandlerProvider é registado, o que tem sido o comportamento de ASP.NET Core desde o lançamento do .NET 8. Portanto, chamar UseAuthorization explicitamente para aplicações do lado do servidor Blazor em .NET 8 ou posteriores é tecnicamente redundante, mas a chamada não é prejudicial. Chamá-lo no código de desenvolvedor depois de já ter sido chamado pelo framework é simplesmente sem efeito.

Observação

Os links de documentação para a fonte de referência .NET normalmente carregam a ramificação padrão do repositório, que representa o desenvolvimento atual para a próxima versão do .NET. Para selecionar uma tag para uma versão específica, use a lista suspensa Alternar entre ramificações ou tags. Para mais informações, consulte Como selecionar uma etiqueta de versão do código-fonte ASP.NET Core (dotnet/AspNetCore.Docs #26205).

O registo da política ocorre como parte da configuração do serviço de autorização no ficheiro da Program aplicação:

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

Nas Blazor Server aplicações, chama UseAuthorization depois da linha que chama UseAuthentication (se existir):

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

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

Nas Blazor Server aplicações, chame UseAuthorization em Startup.Configure depois da linha de código que chama UseAuthentication (se existir):

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

Blazor WebAssembly As aplicações chamam AddAuthorizationCore no Program ficheiro para adicionar serviços de autorização:

builder.Services.AddAuthorizationCore();

Aplique a apólice usando a Policy propriedade no [Authorize] atributo para especificar o nome da apólice. No exemplo seguinte, a EmployeeOnly apólice verifica a presença de uma EmployeeNumber reivindicação sobre a identidade atual:

Para autorização baseada em políticas usando um AuthorizeView componente, use o AuthorizeView.Policy parâmetro 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>

Alternativamente, aplique a apólice usando a Policy propriedade no [Authorize] atributo para especificar o nome da apólice. No exemplo seguinte, a EmployeeOnly apólice verifica a presença de uma EmployeeNumber reivindicação sobre a 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>

Pode especificar uma lista de valores permitidos ao criar uma política. A seguinte política só se aplica a funcionários cujo número de funcionário é 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 genérica de reclamação

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

Avaliar múltiplas políticas

Múltiplas políticas são aplicadas através de múltiplos AuthorizeView componentes. O componente interno exige que o utilizador passe a sua política e todas as políticas dos componentes pais AuthorizeView .

O exemplo a seguir:

  • Requer uma CustomerServiceMember política, que indica que o utilizador está no departamento de atendimento ao cliente da organização porque tem uma Department reclamação com valor de Customer Service.
  • Também requer uma HumanResourcesMember política, que indica que o utilizador pertence ao departamento de recursos humanos da organização porque tem uma Department reivindicação com valor de Human Resources.

No ficheiro da Program aplicação:

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

No ficheiro da Program aplicação:

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 seguinte utiliza 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 seguinte utiliza [Authorize] os 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 apólices mais complicadas, como recolher um pedido de data de nascimento, calcular uma idade a partir dela e depois verificar se a idade é 21 anos ou mais, use RequireAssertion ou escreva gestores de apólices personalizados. Os handlers de políticas personalizados são úteis quando precisa de acesso a serviços injetados por dependências ou quando pretende um componente de autorização reutilizável e testável.

Sensibilidade ao caso da reclamação

Os valores das reclamações são comparados usando StringComparison.Ordinal. Isto significa Admin (maiúsculas A) e admin (minúsculas a) são sempre tratadas como papéis diferentes, independentemente de qual gestor de autenticação estabeleceu a identidade.

Separadamente, a comparação de tipos de reivindicações (usada para localizar reivindicações de função pelo seu tipo, como http://schemas.microsoft.com/ws/2008/06/identity/claims/role) pode ser sensível a maiúsculas minúsculas ou insensíveis 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 utiliza correspondência de tipos de reivindicação sensíveis a maiúsculas minúsculas.

O predefinido ClaimsIdentity fornecido pelo tempo de execução .NET (usado na maioria dos casos, incluindo todos os fluxos baseados em cookie) ainda utiliza correspondência de tipos de declarações não sensível a maiúsculas e minúsculas.

Na prática, esta distinção raramente é relevante para a autorização da função, porque o tipo de reivindicação da função é definido apenas uma vez durante a criação da identidade e mantido de forma consistente. Usa sempre casos consistentes para nomes de funções e tipos de reivindicações para evitar problemas subtis.

Recursos adicionais