Role-based vs Policy-based Authorization în .NET
Cum controlezi corect accesul într-o aplicație ASP.NET Core
Seria: Security by Design în .NET: De la JWT la Certificate-based Authentication
Autentificarea răspunde la întrebarea:
„Cine este utilizatorul?”
Autorizarea răspunde la întrebarea:
„Are voie să facă această acțiune?”
În ASP.NET Core există mai multe moduri de a implementa autorizarea:
-
Role-based authorization
-
Policy-based authorization
-
Resource-based authorization
Folosite corect, aceste mecanisme pot crea un sistem de permisiuni foarte flexibil.
🧩 1️⃣ Role-based Authorization
Role-based authorization este cel mai simplu model.
Accesul este controlat prin roluri precum:
-
Admin
-
Manager
-
User
-
Moderator
În ASP.NET Core se folosește atributul:
[Authorize(Roles = "Admin")]
Exemplu:
[Authorize(Roles = "Admin")]
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteUser(Guid id)
{
await _userService.DeleteUser(id);
return Ok();
}
Doar utilizatorii cu rolul Admin pot accesa endpoint-ul.
Mai multe roluri
[Authorize(Roles = "Admin,Manager")]
Utilizatorul trebuie să aibă cel puțin unul dintre roluri.
Dezavantaje Role-based
Role-based devine rapid limitat:
Probleme frecvente:
❌ prea multe roluri
❌ roluri hardcodate
❌ logică complexă de business
❌ lipsă flexibilitate
Exemplu real:
„Userul poate edita factura doar dacă aparține companiei lui.”
Role-based nu rezolvă asta.
🛡️ 2️⃣ Policy-based Authorization
Policy-based authorization este mult mai flexibil.
În loc de roluri simple, definim politici de securitate.
Configurare în Program.cs:
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("RequireAdmin", policy =>
policy.RequireRole("Admin"));
});
Utilizare:
[Authorize(Policy = "RequireAdmin")]
Politici bazate pe claims
JWT poate conține claims:
{
"role": "Admin",
"department": "Finance"
}
Definim o politică:
options.AddPolicy("FinanceOnly",
policy => policy.RequireClaim("department", "Finance"));
Endpoint:
[Authorize(Policy = "FinanceOnly")]
🧠 3️⃣ Resource-based Authorization
Uneori accesul depinde de resursa accesată.
Exemplu:
Un user poate edita doar facturile companiei sale.
Aceasta nu poate fi rezolvată cu roles.
Trebuie verificat obiectul.
Exemplu:
public async Task<bool> CanEditInvoice(User user, Invoice invoice)
{
return user.CompanyId == invoice.CompanyId;
}
ASP.NET Core permite integrarea acestei logici în sistemul de authorization.
⚙️ 4️⃣ Authorization Handlers
Authorization Handlers permit logică custom.
Definim un requirement:
public class InvoiceOwnerRequirement : IAuthorizationRequirement
{
}
Handler:
public class InvoiceOwnerHandler
: AuthorizationHandler<InvoiceOwnerRequirement, Invoice>
{
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context,
InvoiceOwnerRequirement requirement,
Invoice resource)
{
var userCompanyId = context.User.FindFirst("companyId")?.Value;
if (resource.CompanyId.ToString() == userCompanyId)
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
Înregistrare handler
builder.Services.AddScoped<
IAuthorizationHandler,
InvoiceOwnerHandler>();
Utilizare
await _authorizationService.AuthorizeAsync(
User,
invoice,
new InvoiceOwnerRequirement());
Dacă verificarea trece:
→ acces permis.
🧱 Arhitectură recomandată
În aplicațiile enterprise:
✔ Role-based pentru acces general
✔ Policy-based pentru reguli
✔ Resource-based pentru obiecte
Exemplu:
Admin → acces general
Policy → FinanceOnly
Resource → factura aparține companiei
🚨 Greșeli frecvente
1️⃣ Doar roles peste tot
2️⃣ Roluri hardcodate în cod
3️⃣ Logică de business în controllers
4️⃣ Lipsă verificare resource ownership
5️⃣ Authorization făcut doar în UI
Regulă importantă:
Authorization trebuie făcut pe server.
🎯 Concluzie
Role-based authorization este util pentru scenarii simple.
Dar aplicațiile reale au nevoie de:
✔ policies
✔ handlers
✔ resource-based authorization
ASP.NET Core oferă un sistem foarte puternic pentru asta.
Dacă este folosit corect, poți construi un model de permisiuni enterprise.