ASP.NET Core - Política de Autorização baseada em Claims - II
Hoje veremos como usar a política de autorização baseada em Claims da ASP.NET Core Identity. |
Continuando o artigo anterior veremos como atribuir claims as roles.
Criando roles e atribuindo claims
As roles permitem que você controle o acesso de usuários a diferentes partes do seu aplicativo, tornando mais fácil gerenciar as permissões e restrições de segurança. Por exemplo, você pode criar uma role "administrador" e dar a ela acesso a todas as funcionalidades e recursos do aplicativo, enquanto restringe o acesso de outras roles, como "usuário" ou "visitante".
As roles são geralmente definidas em uma tabela separada do banco de dados (AspNetRoles), que é usada para armazenar as informações de autenticação e autorização do usuário. Você pode criar, atualizar e excluir roles através de APIs fornecidas pela biblioteca Identity, e também pode usar essas roles em conjunto com outras ferramentas de segurança, como as políticas de autorização baseadas em claims.
Para ilustrar vamos alterar a interface ISeedUserClaim incluindo o contrato para gerar uma role e atribuir uma claim a esta role a seguir atribuir um usuário à role criada.
public
interface
ISeedUserClaim { Task SeedUsersClaims(); Task SeedRolesClaims(); } |
A seguir vamos alterar o código da classe SeedUserClaim incluindo o método SeedRolesClaims() :
using Microsoft.AspNetCore.Identity;
using System.Security.Claims;
namespace MvcClaimsPolicyAutorization.SeedUsersClaims;
public class SeedUserClaim : ISeedUserClaim
{
private readonly UserManager<IdentityUser> _userManager;
private readonly RoleManager<IdentityRole> _roleManager;
public SeedUserClaim(UserManager<IdentityUser> userManager,
RoleManager<IdentityRole> roleManager)
{
_userManager = userManager;
_roleManager = roleManager;
}
public async Task SeedUsersClaims()
{
//código ja implementado
// ...
}
public async Task SeedRolesClaims()
{
try
{
//cria Roles
var roleResult = await _roleManager.FindByNameAsync("Admin");
if (roleResult == null)
{
roleResult = new IdentityRole("Admin");
await _roleManager.CreateAsync(roleResult);
}
var roleClaimList = (await _roleManager.GetClaimsAsync(roleResult)).Select(p => p.Type);
if (!roleClaimList.Contains("GerenciaPermissao"))
{
var claimResult = await _roleManager.AddClaimAsync(roleResult,
new Claim("GerenciaPermissao", "true"));
IdentityUser user = await _userManager.FindByEmailAsync("admin@yahoo.com");
if (user == null)
{
user = new IdentityUser()
{
UserName = "admin@yahoo.com",
Email = "admin@yahoo.com",
};
await _userManager.CreateAsync(user, "Numsey#2023");
}
await _userManager.AddToRoleAsync(user, "Admin");
}
}
catch (Exception)
{
throw;
}
}
}
|
Neste código criamos a role Admin e a seguir incluimos a claim 'GerenciarPermissao' a esta role e definimos o valor true, a seguir, incluimos o usuário admin@yahoo.com na role Admin.
o código await _roleManager.GetClaimsAsync(roleResult)).Select(p => p.Type) retorna uma lista de tipos de claims associadas a uma determinada role.
Para incluir a claim na role com o valor true usamos o código:
await _roleManager.AddClaimAsync(roleResult,
new Claim("GerenciaPermissao", "true"));
A seguir vamos invocar o método SeedRolesClaims na classe Program:
... await CriarUsuariosClaims(app);app.Run(); async Task CriarUsuariosClaims(WebApplication app){ var scopedFactory = app.Services.GetService<IServiceScopeFactory>(); using (var scope = scopedFactory.CreateScope()) { var service = scope.ServiceProvider.GetService<ISeedUserClaim>(); //await service.SeedUsersClaims(); await service.SeedRolesClaims(); } } |
Após executar a aplicação este método será invocado e teremos nas tabelas do Identity AspNetRoles e AspNetRoleClaims preenchidas com as seguintes informações:
1- AspNetRoles
2- AspNetRoleClaims
Com isso podemos definir políticas de autorização com base nestas
informações do usuário.
Para ilustrar vamos criar mais uma política chamada RoleBasedClaim para verificar se a claim GerenciaPermissao atribuida é igual a true:
Para isso vamos incluir na classe Program o código abaixo:
...
builder.Services.AddAuthorization(options => }); ... |
Agora podemos aplicar esta política ao atributo Authorize usando a propriedade "Policy" onde temos que especificar o nome da política.
Podemos definir o atributo Authorize seguido da nome da política no controlador ou nos métodos Action:
using
Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; namespace MvcClaimsPolicyAutorization.Controllers; public class DemoController : Controller{ [Authorize(Policy = "IsAdminClaimAccess")] public IActionResult ActionMetodo1() { return View("MinhaPagina"); } [Authorize(Policy = "RoleBasedClaim")]public IActionResult ActionMetodo2() { return View("MinhaPagina"); } } |
Agora somente o usuário admin@yahoo.com que possui a claim GerenciaPermissao com valor igual a true e que poderá acessar MinhaPagina a partir o método Action ActionMetodo2.
A plataforma .NET
permite criar políticas de autorização. Podemos usar políticas pré-configuradas
ou criar uma política personalizada com base em nossos requisitos.
Na autorização baseada em role e na autorização baseada em claims, estamos
usando políticas pré-configuradas, como RequireClaim e
RequireRole.
O método RequireClaim é usado para especificar que um usuário precisa ter uma determinada reivindicação (claim) para acessar um recurso protegido em seu aplicativo.
Já o método RequireRole é semelhante ao RequireClaim, mas em vez de uma claim específica, ele verifica se o usuário possui uma role específica. Por exemplo, você pode usar o método RequireRole para exigir que apenas usuários com a role "administrador" possam acessar um recurso do aplicativo.
Desta forma RequireClaim e RequireRole são políticas pré-definidas da ASP.NET Core Identity que você pode usar para proteger seus recursos.
No entanto, é possível definir suas próprias políticas de autorização personalizadas usando a interface IAuthorizationRequirement e a classe AuthorizationHandler. Isso permite que você crie políticas de autorização sob medida para atender às necessidades de segurança específicas do seu aplicativo.
Para definir sua própria política de autorização personalizada, você precisará seguir os seguintes passos:
Por exemplo, imagine que você deseja criar uma política de autorização personalizada que permite que apenas usuários com idade acima de 18 anos acessem um recurso específico. Você pode criar uma classe que implementa IAuthorizationRequirement para representar essa regra de autorização, e uma classe AuthorizationHandler para avaliar se essa regra é atendida. Em seguida, você pode registrar ess a política de autorização personalizada no serviço de autorização do ASP.NET Core e usá-la nas anotações [Authorize] em seus métodos de controlador ou rotas do aplicativo.
Para mostrar como criar uma politica personalizada vamos criar uma política que vai usar a claim 'CadastradoEm' que criamos no artigo anterior e vamos definir um tempo mínimo de cadastro para poder acessar uma página.
Assim primeiro vamos criar um a pasta Policies no projeto e nesta pasta criar a classe TempoCadastroRequirement que herda de IAuthorizationRequirement :
using
Microsoft.AspNetCore.Authorization; namespace MvcClaimsPolicyAutorization.Policies;public class TempoCadastroRequirement : IAuthorizationRequirement{ public int TempoCadastroMinimo { get; } public TempoCadastroRequirement(int tempoCadastroMinimo) { TempoCadastroMinimo = tempoCadastroMinimo; } } |
A seguir vamos criar a classe TempoCadastroHandler que herda de AuthorizationHandler<T> e T é nossa classe TempoCadastroRequirement :
using Microsoft.AspNetCore.Authorization;
namespace MvcClaimsPolicyAutorization.Policies
{
public class TempoCadastroHandler : AuthorizationHandler<TempoCadastroRequirement>
{
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context,
TempoCadastroRequirement requirement)
{
if (context.User.HasClaim(c => c.Type == "CadastradoEm"))
{
var data = context.User.FindFirst(c => c.Type == "CadastradoEm").Value;
var dataCadastro = DateTime.Parse(data);
double tempoCadastro = await Task.Run(() =>
(DateTime.Now.Date - dataCadastro.Date).TotalDays);
var tempoEmAnos = tempoCadastro / 360;
if (tempoEmAnos >= requirement.TempoCadastroMinimo)
{
context.Succeed(requirement);
}
return;
}
}
}
}
|
Neste código obtemos o valor da claim 'CadastradoEm' e convertemos para o tipo DateTime e a seguir calculamos o tempo de cadastro em anos.
Para concluir verificamos se o valor obtido é maior ou igual ao tempo mínimo que iremos definir quando criarmos a politica de autorização.
A seguir na classe Program vamos incluir uma nova política chamada TempoCadastroMinimo :
builder.Services.AddAuthorization(options => { options.AddPolicy("IsAdminClaimAccess", policy => policy.RequireClaim("CadastradoEm")); options.AddPolicy("IsAdminClaimAccess", policy => policy.RequireClaim("IsAdmin", "true")); options.AddPolicy("RoleBasedClaim", policy => policy.RequireClaim("GerenciaPermissao", "true")); options.AddPolicy("TempoCadastroMinimo", policy => { policy.Requirements.Add(new TempoCadastroRequirement(8)); }); }); |
Definimos que o tempo de cadastro mínimo será de 8 anos.
Agora podemos usar o atributo Authorize e definir qual a politica desejamos aplicar a um Controlador ou método Action:
public
class
DemoController : Controller { [Authorize(Policy = "IsAdminClaimAccess")] public IActionResult ActionMetodo1() { return View("MinhaPagina"); } [Authorize(Policy = "RoleBasedClaim")]public IActionResult ActionMetodo2() { return View("MinhaPagina"); } [Authorize(Policy = "TempoCadastroMinimo")] public IActionResult ActionMetodo3() { return View("MinhaPagina"); } } |
No exemplo o método Action ActionMetodo3() somente será acessado se o usuário tiver a claim CadastradoEm e o tempo minímo de cadastro for superior a 8 anos.
Pegue o projeto aqui: MvcClaimsPolicyAutorization.zip ...
"(Disse Jesus) Eu sou a videira verdadeira, e meu Pai
é o agricultor. Todo ramo que, estando em mim, não dá fruto, ele corta; e
todo que dá fruto ele poda, para que dê mais fruto ainda. "
Joao 15:1
Referências:
NET - Unit of Work - Padrão Unidade de ...