Autorização baseada em recursos no ASP.NET Core
A abordagem de autorização depende do recurso. Por exemplo, apenas o autor de um documento está autorizado a atualizá-lo. Consequentemente, o documento deve ser recuperado do armazenamento de dados antes que a avaliação de autorização possa ocorrer.
A avaliação de atributos ocorre antes da vinculação de dados e antes da execução do manipulador de página ou da ação que carrega o documento. Por esses motivos, a autorização declarativa com um atributo [Authorize]
não é suficiente. Em vez disso, você pode invocar um método de autorização personalizado — um estilo conhecido como autorização imperativa.
Ver ou transferir código de exemplo (como fazer o download).
Criar um aplicativo ASP.NET Core com dados do usuário protegidos por autorização contém um aplicativo de exemplo que usa autorização baseada em recursos.
Use uma autorização imperativa
A autorização é implementada como um serviço de IAuthorizationService e é registrada na coleção de serviços na inicialização do aplicativo. O serviço é disponibilizado por meio de de injeção de dependência para manipuladores de página ou ações.
public class DocumentController : Controller
{
private readonly IAuthorizationService _authorizationService;
private readonly IDocumentRepository _documentRepository;
public DocumentController(IAuthorizationService authorizationService,
IDocumentRepository documentRepository)
{
_authorizationService = authorizationService;
_documentRepository = documentRepository;
}
IAuthorizationService
tem duas AuthorizeAsync
sobrecargas de método: uma que aceita o recurso e o nome da política, e a outra que aceita o recurso e uma lista de requisitos para avaliação.
Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user,
object resource,
IEnumerable<IAuthorizationRequirement> requirements);
Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user,
object resource,
string policyName);
No exemplo a seguir, o recurso a ser protegido é carregado em um objeto Document
personalizado. Uma sobrecarga de AuthorizeAsync
é invocada para determinar se o usuário atual tem permissão para editar o documento fornecido. Uma política de autorização "EditPolicy" personalizada é levada em consideração na decisão. Consulte Autorização personalizada baseada em política para obter mais informações sobre como criar políticas de autorização.
Observação
Os exemplos de código a seguir assumem que a autenticação foi executada e definem a propriedade User
.
public async Task<IActionResult> OnGetAsync(Guid documentId)
{
Document = _documentRepository.Find(documentId);
if (Document == null)
{
return new NotFoundResult();
}
var authorizationResult = await _authorizationService
.AuthorizeAsync(User, Document, "EditPolicy");
if (authorizationResult.Succeeded)
{
return Page();
}
else if (User.Identity.IsAuthenticated)
{
return new ForbidResult();
}
else
{
return new ChallengeResult();
}
}
Escrever um manipulador baseado em recursos
Escrever um manipulador para autorização baseada em recursos não é muito diferente de escrever um manipulador de requisitos simples. Crie uma classe de requisito personalizada e implemente uma classe de manipulador de requisitos. Para obter mais informações sobre como criar uma classe de requisito, consulte Requirements.
A classe handler especifica o requisito e o tipo de recurso. Por exemplo, um manipulador utilizando um SameAuthorRequirement
e um recurso Document
segue:
public class DocumentAuthorizationHandler :
AuthorizationHandler<SameAuthorRequirement, Document>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
SameAuthorRequirement requirement,
Document resource)
{
if (context.User.Identity?.Name == resource.Author)
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
public class SameAuthorRequirement : IAuthorizationRequirement { }
No exemplo anterior, imagine que SameAuthorRequirement
é um caso especial de uma classe SpecificAuthorRequirement
mais genérica. A classe SpecificAuthorRequirement
(não mostrada) contém uma propriedade Name
que representa o nome do autor. A propriedade Name
pode ser atribuída ao utilizador atual.
Registre o requisito e o manipulador em Program.cs
:
builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("EditPolicy", policy =>
policy.Requirements.Add(new SameAuthorRequirement()));
});
builder.Services.AddSingleton<IAuthorizationHandler, DocumentAuthorizationHandler>();
builder.Services.AddSingleton<IAuthorizationHandler, DocumentAuthorizationCrudHandler>();
builder.Services.AddScoped<IDocumentRepository, DocumentRepository>();
Requisitos operacionais
Se você estiver tomando decisões com base nos resultados das operações CRUD (Criar, Ler, Atualizar, Excluir), use a classe auxiliar OperationAuthorizationRequirement. Essa classe permite que você escreva um único manipulador em vez de uma classe individual para cada tipo de operação. Para usá-lo, forneça alguns nomes de operação:
public static class Operations
{
public static OperationAuthorizationRequirement Create =
new OperationAuthorizationRequirement { Name = nameof(Create) };
public static OperationAuthorizationRequirement Read =
new OperationAuthorizationRequirement { Name = nameof(Read) };
public static OperationAuthorizationRequirement Update =
new OperationAuthorizationRequirement { Name = nameof(Update) };
public static OperationAuthorizationRequirement Delete =
new OperationAuthorizationRequirement { Name = nameof(Delete) };
}
O manipulador é implementado da seguinte forma, usando um requisito de OperationAuthorizationRequirement
e um recurso Document
:
public class DocumentAuthorizationCrudHandler :
AuthorizationHandler<OperationAuthorizationRequirement, Document>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
OperationAuthorizationRequirement requirement,
Document resource)
{
if (context.User.Identity?.Name == resource.Author &&
requirement.Name == Operations.Read.Name)
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
O manipulador anterior valida a operação usando o recurso, a identidade do usuário e a propriedade Name
do requisito.
Imponha restrições e proíba com um manipulador de recursos de operação
Esta secção mostra como são processados os resultados das ações de desafiar e proibir, e como estas se diferenciam.
Para chamar um manipulador de recursos operacionais, especifique a operação ao invocar AuthorizeAsync
em seu manipulador de página ou ação. O exemplo a seguir determina se o usuário autenticado tem permissão para exibir o documento fornecido.
Observação
Os exemplos de código a seguir assumem que a autenticação foi executada e definem a propriedade User
.
public async Task<IActionResult> OnGetAsync(Guid documentId)
{
Document = _documentRepository.Find(documentId);
if (Document == null)
{
return new NotFoundResult();
}
var authorizationResult = await _authorizationService
.AuthorizeAsync(User, Document, Operations.Read);
if (authorizationResult.Succeeded)
{
return Page();
}
else if (User.Identity.IsAuthenticated)
{
return new ForbidResult();
}
else
{
return new ChallengeResult();
}
}
Se a autorização for bem-sucedida, a página para exibir o documento será retornada. Se a autorização falhar, mas o usuário for autenticado, retornar ForbidResult
informará qualquer middleware de autenticação de que a autorização falhou. Um ChallengeResult
é retornado quando a autenticação deve ser executada. Para clientes de navegador interativos, pode ser apropriado redirecionar o usuário para uma página de login.
A abordagem de autorização depende do recurso. Por exemplo, apenas o autor de um documento está autorizado a atualizá-lo. Consequentemente, o documento deve ser recuperado do armazenamento de dados antes que a avaliação de autorização possa ocorrer.
A avaliação de atributos ocorre antes da vinculação de dados e antes da execução do manipulador de página ou da ação que carrega o documento. Por esses motivos, a autorização declarativa com um atributo [Authorize]
não é suficiente. Em vez disso, você pode invocar um método de autorização personalizado — um estilo conhecido como autorização imperativa.
Ver ou descarregar exemplo de código (como descarregar).
Criar um aplicativo ASP.NET Core com dados do usuário protegidos por autorização contém um aplicativo de exemplo que usa autorização baseada em recursos.
Utilizar autorização imperativa
A autorização é implementada como um serviço IAuthorizationService e é registrada na coleção de serviços dentro da classe Startup
. O serviço é disponibilizado através de injeção de dependência para manipuladores de página ou ações.
public class DocumentController : Controller
{
private readonly IAuthorizationService _authorizationService;
private readonly IDocumentRepository _documentRepository;
public DocumentController(IAuthorizationService authorizationService,
IDocumentRepository documentRepository)
{
_authorizationService = authorizationService;
_documentRepository = documentRepository;
}
IAuthorizationService
tem duas AuthorizeAsync
sobrecargas de método: uma aceitando o recurso e o nome da política e a outra aceitando o recurso e uma lista de requisitos para avaliar.
Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user,
object resource,
IEnumerable<IAuthorizationRequirement> requirements);
Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user,
object resource,
string policyName);
No exemplo a seguir, o recurso a ser protegido é carregado em um objeto Document
personalizado. Uma sobrecarga de AuthorizeAsync
é invocada para determinar se o usuário atual tem permissão para editar o documento fornecido. Uma política de autorização "EditPolicy" personalizada é levada em consideração na decisão. Consulte autorização personalizada baseada em políticas para saber mais sobre a criação de políticas de autorização.
Observação
Os exemplos de código a seguir assumem que a autenticação foi executada e definem a propriedade User
.
public async Task<IActionResult> OnGetAsync(Guid documentId)
{
Document = _documentRepository.Find(documentId);
if (Document == null)
{
return new NotFoundResult();
}
var authorizationResult = await _authorizationService
.AuthorizeAsync(User, Document, "EditPolicy");
if (authorizationResult.Succeeded)
{
return Page();
}
else if (User.Identity.IsAuthenticated)
{
return new ForbidResult();
}
else
{
return new ChallengeResult();
}
}
Escrever um manipulador baseado em recursos
Escrever um manipulador para autorização baseada em recursos não é muito diferente de escrever um manipulador de requisitos simples. Crie uma classe de requisito personalizada e implemente uma classe de manipulador de requisitos. Para obter mais informações sobre como criar uma classe de requisito, consulte Requirements.
A classe handler especifica o requisito e o tipo de recurso. Por exemplo, um manipulador utilizando um SameAuthorRequirement
e um recurso Document
segue:
public class DocumentAuthorizationHandler :
AuthorizationHandler<SameAuthorRequirement, Document>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
SameAuthorRequirement requirement,
Document resource)
{
if (context.User.Identity?.Name == resource.Author)
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
public class SameAuthorRequirement : IAuthorizationRequirement { }
No exemplo anterior, imagine que SameAuthorRequirement
é um caso especial de uma classe SpecificAuthorRequirement
mais genérica. A classe SpecificAuthorRequirement
(não mostrada) contém uma propriedade Name
que representa o nome do autor. A propriedade Name
pode ser definida como o utilizador atual.
Registre o requisito e o manipulador em Startup.ConfigureServices
:
services.AddControllersWithViews();
services.AddRazorPages();
services.AddAuthorization(options =>
{
options.AddPolicy("EditPolicy", policy =>
policy.Requirements.Add(new SameAuthorRequirement()));
});
services.AddSingleton<IAuthorizationHandler, DocumentAuthorizationHandler>();
services.AddSingleton<IAuthorizationHandler, DocumentAuthorizationCrudHandler>();
services.AddScoped<IDocumentRepository, DocumentRepository>();
Requisitos operacionais
Se você estiver tomando decisões com base nos resultados das operações CRUD (Criar, Ler, Atualizar, Excluir), use a classe auxiliar OperationAuthorizationRequirement. Essa classe permite que você escreva um único manipulador em vez de uma classe individual para cada tipo de operação. Para usá-lo, forneça alguns nomes de operação:
public static class Operations
{
public static OperationAuthorizationRequirement Create =
new OperationAuthorizationRequirement { Name = nameof(Create) };
public static OperationAuthorizationRequirement Read =
new OperationAuthorizationRequirement { Name = nameof(Read) };
public static OperationAuthorizationRequirement Update =
new OperationAuthorizationRequirement { Name = nameof(Update) };
public static OperationAuthorizationRequirement Delete =
new OperationAuthorizationRequirement { Name = nameof(Delete) };
}
O manipulador é implementado da seguinte forma, usando um requisito de OperationAuthorizationRequirement
e um recurso Document
:
public class DocumentAuthorizationCrudHandler :
AuthorizationHandler<OperationAuthorizationRequirement, Document>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
OperationAuthorizationRequirement requirement,
Document resource)
{
if (context.User.Identity?.Name == resource.Author &&
requirement.Name == Operations.Read.Name)
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
O manipulador anterior valida a operação usando o recurso, a identidade do usuário e a propriedade Name
do requisito.
Desafie e proíba com um gestor de recurso operacional
Esta seção mostra como os resultados das ações de desafio e de proibição são processados e como o desafio e a proibição diferem.
Para chamar um manipulador de recursos operacionais, especifique a operação ao invocar AuthorizeAsync
em seu manipulador de página ou ação. O exemplo a seguir determina se o usuário autenticado tem permissão para exibir o documento fornecido.
Observação
Os exemplos de código a seguir assumem que a autenticação foi executada e definem a propriedade User
.
public async Task<IActionResult> OnGetAsync(Guid documentId)
{
Document = _documentRepository.Find(documentId);
if (Document == null)
{
return new NotFoundResult();
}
var authorizationResult = await _authorizationService
.AuthorizeAsync(User, Document, Operations.Read);
if (authorizationResult.Succeeded)
{
return Page();
}
else if (User.Identity.IsAuthenticated)
{
return new ForbidResult();
}
else
{
return new ChallengeResult();
}
}
Se a autorização for bem-sucedida, a página para exibir o documento será retornada. Se a autorização falhar, mas o usuário for autenticado, retornar ForbidResult
informará qualquer middleware de autenticação de que a autorização falhou. Um ChallengeResult
é retornado quando a autenticação deve ser executada. Para clientes de navegador interativos, pode ser apropriado redirecionar o usuário para uma página de login.
A abordagem de autorização depende do recurso. Por exemplo, apenas o autor de um documento está autorizado a atualizá-lo. Consequentemente, o documento deve ser recuperado do armazenamento de dados antes que a avaliação de autorização possa ocorrer.
A avaliação de atributos ocorre antes da vinculação de dados e antes da execução do manipulador de página ou da ação que carrega o documento. Por esses motivos, a autorização declarativa com um atributo [Authorize]
não é suficiente. Em vez disso, você pode invocar um método de autorização personalizado — um estilo conhecido como autorização imperativa.
Ver ou transferir o código de exemplo (como transferir).
Criar um aplicativo ASP.NET Core com dados do usuário protegidos por autorização contém um aplicativo de exemplo que usa autorização baseada em recursos.
Use autorização imperativa
A autorização é implementada como um serviço IAuthorizationService e é registrada na coleção de serviços dentro da classe Startup
. O serviço é disponibilizado por meio de injeção de dependência para manipuladores de página ou ações.
public class DocumentController : Controller
{
private readonly IAuthorizationService _authorizationService;
private readonly IDocumentRepository _documentRepository;
public DocumentController(IAuthorizationService authorizationService,
IDocumentRepository documentRepository)
{
_authorizationService = authorizationService;
_documentRepository = documentRepository;
}
IAuthorizationService
tem duas AuthorizeAsync
sobrecargas de método: uma que aceita o recurso e o nome da política e a outra que aceita o recurso e uma lista de requisitos a serem avaliados.
Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user,
object resource,
IEnumerable<IAuthorizationRequirement> requirements);
Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user,
object resource,
string policyName);
No exemplo a seguir, o recurso a ser protegido é carregado em um objeto Document
personalizado. Uma sobrecarga de AuthorizeAsync
é invocada para determinar se o usuário atual tem permissão para editar o documento fornecido. Uma política de autorização "EditPolicy" personalizada é levada em consideração na decisão. Consulte a autorização personalizada baseada em políticas para obter mais informações sobre como criar políticas de autorização.
Observação
Os exemplos de código a seguir assumem que a autenticação foi executada e definem a propriedade User
.
public async Task<IActionResult> OnGetAsync(Guid documentId)
{
Document = _documentRepository.Find(documentId);
if (Document == null)
{
return new NotFoundResult();
}
var authorizationResult = await _authorizationService
.AuthorizeAsync(User, Document, "EditPolicy");
if (authorizationResult.Succeeded)
{
return Page();
}
else if (User.Identity.IsAuthenticated)
{
return new ForbidResult();
}
else
{
return new ChallengeResult();
}
}
Escrever um manipulador baseado em recursos
Escrever um manipulador para autorização baseada em recursos não é muito diferente de escrever um manipulador de requisitos simples. Crie uma classe de requisito personalizada e implemente uma classe de manipulador de requisitos. Para obter mais informações sobre como criar uma classe de requisito, consulte Requirements.
A classe handler especifica o requisito e o tipo de recurso. Por exemplo, um manipulador que utiliza um recurso SameAuthorRequirement
e o recurso Document
é o seguinte:
public class DocumentAuthorizationHandler :
AuthorizationHandler<SameAuthorRequirement, Document>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
SameAuthorRequirement requirement,
Document resource)
{
if (context.User.Identity?.Name == resource.Author)
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
public class SameAuthorRequirement : IAuthorizationRequirement { }
No exemplo anterior, imagine que SameAuthorRequirement
é um caso especial de uma classe SpecificAuthorRequirement
mais genérica. A classe SpecificAuthorRequirement
(não mostrada) contém uma propriedade Name
que representa o nome do autor. A propriedade Name
pode ser definida para o utilizador atual.
Registre o requisito e o manipulador em Startup.ConfigureServices
:
services.AddMvc();
services.AddAuthorization(options =>
{
options.AddPolicy("EditPolicy", policy =>
policy.Requirements.Add(new SameAuthorRequirement()));
});
services.AddSingleton<IAuthorizationHandler, DocumentAuthorizationHandler>();
services.AddSingleton<IAuthorizationHandler, DocumentAuthorizationCrudHandler>();
services.AddScoped<IDocumentRepository, DocumentRepository>();
Requisitos operacionais
Se você estiver tomando decisões com base nos resultados das operações CRUD (Criar, Ler, Atualizar, Excluir), use a classe auxiliar OperationAuthorizationRequirement. Essa classe permite que você escreva um único manipulador em vez de uma classe individual para cada tipo de operação. Para usá-lo, forneça alguns nomes de operação:
public static class Operations
{
public static OperationAuthorizationRequirement Create =
new OperationAuthorizationRequirement { Name = nameof(Create) };
public static OperationAuthorizationRequirement Read =
new OperationAuthorizationRequirement { Name = nameof(Read) };
public static OperationAuthorizationRequirement Update =
new OperationAuthorizationRequirement { Name = nameof(Update) };
public static OperationAuthorizationRequirement Delete =
new OperationAuthorizationRequirement { Name = nameof(Delete) };
}
O manipulador é implementado da seguinte forma, usando um requisito de OperationAuthorizationRequirement
e um recurso Document
:
public class DocumentAuthorizationCrudHandler :
AuthorizationHandler<OperationAuthorizationRequirement, Document>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
OperationAuthorizationRequirement requirement,
Document resource)
{
if (context.User.Identity?.Name == resource.Author &&
requirement.Name == Operations.Read.Name)
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
O manipulador anterior valida a operação usando o recurso, a identidade do usuário e a propriedade Name
do requisito.
Desafie e proíba com um manipulador de recursos operacionais
Esta seção mostra como os resultados das ações de desafiar e proibir são processados e como estas ações diferem.
Para chamar um manipulador de recursos operacionais, especifique a operação ao invocar AuthorizeAsync
em seu manipulador de página ou ação. O exemplo a seguir determina se o usuário autenticado tem permissão para exibir o documento fornecido.
Observação
Os exemplos de código a seguir assumem que a autenticação foi executada e definem a propriedade User
.
public async Task<IActionResult> OnGetAsync(Guid documentId)
{
Document = _documentRepository.Find(documentId);
if (Document == null)
{
return new NotFoundResult();
}
var authorizationResult = await _authorizationService
.AuthorizeAsync(User, Document, Operations.Read);
if (authorizationResult.Succeeded)
{
return Page();
}
else if (User.Identity.IsAuthenticated)
{
return new ForbidResult();
}
else
{
return new ChallengeResult();
}
}
Se a autorização for bem-sucedida, a página para exibir o documento será retornada. Se a autorização falhar, mas o usuário for autenticado, retornar ForbidResult
informará qualquer middleware de autenticação de que a autorização falhou. Um ChallengeResult
é retornado quando a autenticação deve ser executada. Para clientes de navegador interativos, pode ser apropriado redirecionar o usuário para uma página de login.