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

Quando uma identidade é criada, ela pode receber uma ou mais declarações emitidas por uma parte confiável. Uma declaração é um par de valores de nome que representa o titular, não o que o titular pode fazer. Por exemplo, você pode ter uma carteira de motorista, emitida por uma autoridade local de habilitação. Sua carteira de motorista tem sua data de nascimento. Nesse caso, o nome da declaração seria DateOfBirth, o valor da declaração seria a data de nascimento, por exemplo 8th June 1970, e o emissor seria a autoridade de habilitação. Basicamente, a autorização baseada em declarações verifica o valor de uma declaração e permite o acesso a um recurso com base nesse valor. Por exemplo, se você quiser acessar um clube noturno, o processo de autorização poderá ser:

O agente de segurança da porta avaliaria o valor da declaração de data de nascimento e se ele confia no emissor (a autoridade de habilitação) antes de conceder-lhe acesso.

Uma identidade pode conter várias declarações com vários valores e pode conter várias declarações do mesmo tipo.

Adicionar verificações de declarações

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

  • São declarativos.
  • São aplicados a Razor Pages, controladores ou ações dentro de um controlador.
  • Não pode ser aplicado no nível do manipulador do Razor Page, eles devem ser aplicados ao Page.

As declarações no código especificam declarações que o usuário atual deve possuir e, opcionalmente, o valor que a declaração deve conter para acessar o recurso solicitado. Os requisitos das declarações são baseados em política; o desenvolvedor deve criar e registrar uma política expressando os requisitos das declarações.

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

Compile e registre a política e chame UseAuthorization. O registro da política ocorre como parte da configuração do serviço de autorização, normalmente no arquivo Program.cs:

var builder = WebApplication.CreateBuilder(args);

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

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

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

Nesse caso, a política EmployeeOnly verifica a presença de uma declaração EmployeeNumber na identidade atual.

Aplique a política usando a propriedade Policy no atributo [Authorize] para especificar o nome da política.

[Authorize(Policy = "EmployeeOnly")]
public IActionResult VacationBalance()
{
    return View();
}

O atributo [Authorize] pode ser aplicado a um controlador inteiro ou a uma página do Razor, somente nos casos em que as identidades correspondentes à política tenham acesso permitido a qualquer Ação no controlador.

[Authorize(Policy = "EmployeeOnly")]
public class VacationController : Controller
{
    public IActionResult Index()
    {
        return View();
    }

    public ActionResult VacationBalance()
    {
        return View();
    }

    [AllowAnonymous]
    public ActionResult VacationPolicy()
    {
        return View();
    }
}

O código a seguir aplica o atributo [Authorize] a um Razor Page:

[Authorize(Policy = "EmployeeOnly")]
public class IndexModel : PageModel
{
    public void OnGet()
    {

    }
}

As políticas não podem ser aplicadas no nível do manipulador do Razor Page, elas devem ser aplicadas ao Page.

Se você tiver um controlador protegido pelo atributo [Authorize], mas quiser permitir o acesso anônimo a ações específicas, aplique o atributo AllowAnonymousAttribute.

[Authorize(Policy = "EmployeeOnly")]
public class VacationController : Controller
{
    public IActionResult Index()
    {
        return View();
    }

    public ActionResult VacationBalance()
    {
        return View();
    }

    [AllowAnonymous]
    public ActionResult VacationPolicy()
    {
        return View();
    }
}

Como as políticas não podem ser aplicadas no nível do manipulador do Razor Page, recomendamos usar um controlador quando as políticas devem ser aplicadas no nível do manipulador de página. O restante do aplicativo que não requer políticas no nível do manipulador do Razor Page pode usar Razor Pages.

A maioria das declarações vem com um valor. Você pode especificar uma lista de valores permitidos ao criar a política. O exemplo a seguir só teria êxito para funcionários cujo número de funcionário fosse 1, 2, 3, 4 ou 5.

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

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

Se o valor da declaração não for um único valor ou uma transformação for necessária, use RequireAssertion. Para obter mais informações, consulte Usar uma função para cumprir uma política.

Avaliação de várias políticas

Se várias políticas forem aplicadas nos níveis de controlador e ação, todas as políticas deverão ser aprovadas antes que o acesso seja concedido:

[Authorize(Policy = "EmployeeOnly")]
public class SalaryController : Controller
{
    public IActionResult Index()
    {
        return View();
    }

    public IActionResult Payslip()
    {
        return View();
    }

    [Authorize(Policy = "HumanResources")]
    public IActionResult UpdateSalary()
    {
        return View();
    }
}

No exemplo anterior, qualquer identidade que atenda à política EmployeeOnly pode acessar a ação Payslip conforme essa política é imposta no controlador. No entanto, para chamar a ação UpdateSalary, a identidade deve cumprir ambas as políticas EmployeeOnly e HumanResources.

Se você quiser políticas mais complicadas, como usar uma declaração de data de nascimento, calcular uma idade com ela, verificar se a idade é 21 anos ou mais, você precisará escrever manipuladores de política personalizados.

No exemplo a seguir, ambos os métodos de manipulador de página devem cumprir ambas a política EmployeeOnly e a política HumanResources:

[Authorize(Policy = "EmployeeOnly")]
[Authorize(Policy = "HumanResources")]
public class SalaryModel : PageModel
{
    public ContentResult OnGetPayStub()
    {
        return Content("OnGetPayStub");
    }

    public ContentResult OnGetSalary()
    {
        return Content("OnGetSalary");
    }
}

Quando uma identidade é criada, ela pode receber uma ou mais declarações emitidas por uma parte confiável. Uma declaração é um par de valores de nome que representa o titular, não o que o titular pode fazer. Por exemplo, você pode ter uma carteira de motorista, emitida por uma autoridade local de habilitação. Sua carteira de motorista tem sua data de nascimento. Nesse caso, o nome da declaração seria DateOfBirth, o valor da declaração seria a data de nascimento, por exemplo 8th June 1970, e o emissor seria a autoridade de habilitação. Basicamente, a autorização baseada em declarações verifica o valor de uma declaração e permite o acesso a um recurso com base nesse valor. Por exemplo, se você quiser acessar um clube noturno, o processo de autorização poderá ser:

O agente de segurança da porta avaliaria o valor da declaração de data de nascimento e se ele confia no emissor (a autoridade de habilitação) antes de conceder-lhe acesso.

Uma identidade pode conter várias declarações com vários valores e pode conter várias declarações do mesmo tipo.

Adicionar verificações de declarações

As verificações de autorização baseadas em declaração são declarativas, ou seja, o desenvolvedor as insere em seu código, em um controlador ou em uma ação dentro de um controlador, especificando declarações em que o usuário atual deve ter e, opcionalmente, o valor que a declaração deve conter para acessar o recurso solicitado. Os requisitos de declarações são baseados em política, o desenvolvedor deve criar e registrar uma política expressando os requisitos de declarações.

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

Compile e registre a política. Isso ocorre como parte da configuração do serviço de autorização, que normalmente participa do ConfigureServices() no arquivo Startup.cs.

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews();
    services.AddRazorPages();

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

Chame UseAuthorization em Configure. O código a seguir é gerado pelos modelos de aplicativo Web ASP.NET Core:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseMigrationsEndPoint();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

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

    app.UseRouting();

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

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

Nesse caso, a política EmployeeOnly verifica a presença de uma declaração EmployeeNumber na identidade atual.

Em seguida, você aplica a política usando a propriedade Policy no atributo [Authorize] para especificar o nome da política;

[Authorize(Policy = "EmployeeOnly")]
public IActionResult VacationBalance()
{
    return View();
}

O atributo [Authorize] pode ser aplicado a um controlador inteiro, nesta instância, somente as identidades correspondentes à política terão acesso a qualquer Ação no controlador.

[Authorize(Policy = "EmployeeOnly")]
public class VacationController : Controller
{
    public ActionResult VacationBalance()
    {
    }
}

Se você tiver um controlador protegido pelo atributo [Authorize], mas quiser permitir o acesso anônimo a ações específicas, aplique o atributo AllowAnonymousAttribute.

[Authorize(Policy = "EmployeeOnly")]
public class VacationController : Controller
{
    public ActionResult VacationBalance()
    {
    }

    [AllowAnonymous]
    public ActionResult VacationPolicy()
    {
    }
}

A maioria das declarações vem com um valor. Você pode especificar uma lista de valores permitidos ao criar a política. O exemplo a seguir só teria êxito para funcionários cujo número de funcionários era 1, 2, 3, 4 ou 5.

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews();
    services.AddRazorPages();

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

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

Se o valor da declaração não for um único valor ou uma transformação for necessária, use RequireAssertion. Para obter mais informações, consulte Usar uma função para cumprir uma política.

Avaliação de várias políticas

Se você aplicar várias políticas a um controlador ou ação, todas as políticas deverão ser aprovadas antes que o acesso seja concedido. Por exemplo:

[Authorize(Policy = "EmployeeOnly")]
public class SalaryController : Controller
{
    public ActionResult Payslip()
    {
    }

    [Authorize(Policy = "HumanResources")]
    public ActionResult UpdateSalary()
    {
    }
}

No exemplo acima, qualquer identidade que atenda à política EmployeeOnly pode acessar a ação Payslip conforme essa política é imposta no controlador. No entanto, para chamar a ação UpdateSalary, a identidade deve cumprir ambas a política EmployeeOnly e a política HumanResources.

Se você quiser políticas mais complicadas, como tomar uma declaração de data de nascimento, calcular uma idade a partir dela, verificar se a idade é 21 anos ou mais, então você precisa escrever manipuladores de política personalizados.

Quando uma identidade é criada, ela pode receber uma ou mais declarações emitidas por uma parte confiável. Uma declaração é um par de valores de nome que representa o titular, não o que o titular pode fazer. Por exemplo, você pode ter uma carteira de motorista, emitida por uma autoridade local de habilitação. Sua carteira de motorista tem sua data de nascimento. Nesse caso, o nome da declaração seria DateOfBirth, o valor da declaração seria a data de nascimento, por exemplo 8th June 1970, e o emissor seria a autoridade de habilitação. Basicamente, a autorização baseada em declarações verifica o valor de uma declaração e permite o acesso a um recurso com base nesse valor. Por exemplo, se você quiser acessar um clube noturno, o processo de autorização poderá ser:

O agente de segurança da porta avaliaria o valor da declaração de data de nascimento e se ele confia no emissor (a autoridade de habilitação) antes de conceder-lhe acesso.

Uma identidade pode conter várias declarações com vários valores e pode conter várias declarações do mesmo tipo.

Adicionar verificações de declarações

As verificações de autorização baseadas em declaração são declarativas, ou seja, o desenvolvedor as insere em seu código, em um controlador ou em uma ação dentro de um controlador, especificando declarações em que o usuário atual deve ter e, opcionalmente, o valor que a declaração deve conter para acessar o recurso solicitado. Os requisitos de declarações são baseados em política, o desenvolvedor deve criar e registrar uma política expressando os requisitos de declarações.

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

Compile e registre a política. Isso ocorre como parte da configuração do serviço de autorização, que normalmente participa do ConfigureServices() no arquivo Startup.cs.

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews();
    services.AddRazorPages();

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

Nesse caso, a política EmployeeOnly verifica a presença de uma declaração EmployeeNumber na identidade atual.

Em seguida, você aplica a política usando a propriedade Policy no atributo [Authorize] para especificar o nome da política;

[Authorize(Policy = "EmployeeOnly")]
public IActionResult VacationBalance()
{
    return View();
}

O atributo [Authorize] pode ser aplicado a um controlador inteiro, nesta instância, somente as identidades correspondentes à política terão acesso a qualquer Ação no controlador.

[Authorize(Policy = "EmployeeOnly")]
public class VacationController : Controller
{
    public ActionResult VacationBalance()
    {
    }
}

Se você tiver um controlador protegido pelo atributo [Authorize], mas quiser permitir o acesso anônimo a ações específicas, aplique o atributo AllowAnonymousAttribute.

[Authorize(Policy = "EmployeeOnly")]
public class VacationController : Controller
{
    public ActionResult VacationBalance()
    {
    }

    [AllowAnonymous]
    public ActionResult VacationPolicy()
    {
    }
}

A maioria das declarações vem com um valor. Você pode especificar uma lista de valores permitidos ao criar a política. O exemplo a seguir só teria êxito para funcionários cujo número de funcionários era 1, 2, 3, 4 ou 5.

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews();
    services.AddRazorPages();

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

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

Se o valor da declaração não for um único valor ou uma transformação for necessária, use RequireAssertion. Para obter mais informações, consulte Usar uma função para cumprir uma política.

Avaliação de várias políticas

Se você aplicar várias políticas a um controlador ou ação, todas as políticas deverão ser aprovadas antes que o acesso seja concedido. Por exemplo:

[Authorize(Policy = "EmployeeOnly")]
public class SalaryController : Controller
{
    public ActionResult Payslip()
    {
    }

    [Authorize(Policy = "HumanResources")]
    public ActionResult UpdateSalary()
    {
    }
}

No exemplo acima, qualquer identidade que atenda à política EmployeeOnly pode acessar a ação Payslip conforme essa política é imposta no controlador. No entanto, para chamar a ação UpdateSalary, a identidade deve cumprir ambas a política EmployeeOnly e a política HumanResources.

Se você quiser políticas mais complicadas, como tomar uma declaração de data de nascimento, calcular uma idade a partir dela, verificar se a idade é 21 anos ou mais, então você precisa escrever manipuladores de política personalizados.