Autorización basada en roles en ASP.NET Core

Cuando se crea una identidad, puede pertenecer a uno o varios roles. Por ejemplo, Tracy puede pertenecer a los roles Administrator y User, mientras que Scott solo puede pertenecer al rol User. La forma en que se crean y administran estos roles depende de la memoria auxiliar del proceso de autorización. Los roles se exponen al desarrollador a través del método IsInRole en la clase ClaimsPrincipal. AddRoles se debe agregar a los servicios de rol.

Aunque los roles son notificaciones, no todas las notificaciones son roles. Dependiendo del emisor de identidad, un rol puede ser una colección de usuarios que pueden aplicar notificaciones para los miembros del grupo, así como una notificación real en una identidad. Sin embargo, las notificaciones están pensadas para ser información sobre un usuario individual. El uso de roles para agregar notificaciones a un usuario puede confundir el límite entre el usuario y sus notificaciones individuales. Esta confusión es la razón por la que las plantillas de SPA no están diseñadas en torno a roles. Además, para las organizaciones que migran desde un sistema heredado local, la proliferación de roles a lo largo de los años puede significar que una notificación de rol puede ser demasiado grande para que las SPA puedan usar un token. Para proteger las SPA, consulte Uso de Identity para proteger un back-end de API web para SPA.

Agregar servicios de rol a Identity

Registre los servicios de autorización basados en roles en Program.cs llamando a AddRoles con el tipo de rol en la configuración de la aplicación de Identity. El tipo de rol en el ejemplo siguiente es IdentityRole:

builder.Services.AddDefaultIdentity<IdentityUser>( ... )
    .AddRoles<IdentityRole>()
    ...

El código anterior requiere el paquete Microsoft.AspNetCore.Identity.UI y una directiva using para Microsoft.AspNetCore.Identity.

Adición de comprobaciones de roles

Comprobaciones de autorización basadas en roles:

  • Son declarativas y especifican los roles de los que el usuario actual debe ser miembro para tener acceso al recurso solicitado.
  • Se aplican a Razor Pages, controladores o acciones dentro de un controlador.
  • No se pueden aplicar en el nivel de controlador de Razor Page, deben aplicarse a la página.

Por ejemplo, el código siguiente limita el acceso a las acciones de AdministrationController para los usuarios que son miembros del rol Administrator:

[Authorize(Roles = "Administrator")]
public class AdministrationController : Controller
{
    public IActionResult Index() =>
        Content("Administrator");
}

Se pueden especificar varios roles como una lista separada por comas:

[Authorize(Roles = "HRManager,Finance")]
public class SalaryController : Controller
{
    public IActionResult Payslip() =>
                    Content("HRManager || Finance");
}

SalaryController solo es accesible para los usuarios que son miembros del rol HRManagero del rol Finance.

Cuando se aplican varios atributos, un usuario que accede debe ser miembro de todos los roles especificados. En el ejemplo siguiente se requiere que un usuario sea miembro del rol, el rol PowerUser y el rol ControlPanelUser:

[Authorize(Roles = "PowerUser")]
[Authorize(Roles = "ControlPanelUser")]
public class ControlPanelController : Controller
{
    public IActionResult Index() =>
        Content("PowerUser && ControlPanelUser");
}

El acceso a una acción se puede limitar mediante la aplicación de atributos de autorización de roles adicionales en el nivel de acción:

[Authorize(Roles = "Administrator, PowerUser")]
public class ControlAllPanelController : Controller
{
    public IActionResult SetTime() =>
        Content("Administrator || PowerUser");

    [Authorize(Roles = "Administrator")]
    public IActionResult ShutDown() =>
        Content("Administrator only");
}

En el controlador ControlAllPanelController anterior:

  • Los miembros del rol Administrator o del rol PowerUser pueden acceder al controlador y a la acción SetTime.
  • Solo los miembros del rol Administrator pueden acceder a la acción ShutDown.

Un controlador puede estar bloqueado, pero permitir el acceso anónimo y no autenticado a acciones individuales:

[Authorize]
public class Control3PanelController : Controller
{
    public IActionResult SetTime() =>
        Content("[Authorize]");

    [AllowAnonymous]
    public IActionResult Login() =>
        Content("[AllowAnonymous]");
}

Para Razor Pages, [Authorize] se puede aplicar mediante:

[Authorize(Policy = "RequireAdministratorRole")]
public class UpdateModel : PageModel
{
    public IActionResult OnPost() =>
         Content("OnPost RequireAdministratorRole");
}

Importante

Los atributos de filtro, incluidos AuthorizeAttribute, solo se pueden aplicar a PageModel y no se pueden aplicar a métodos de controlador de página específicos.

Comprobaciones de roles basadas en directivas

Los requisitos de rol también se pueden expresar mediante la sintaxis de directiva, donde un desarrollador registra una directiva en el inicio de la aplicación como parte de la configuración del servicio de autorización. Esto suele ocurrir en el archivo Program.cs:

var builder = WebApplication.CreateBuilder(args);

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

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("RequireAdministratorRole",
         policy => policy.RequireRole("Administrator"));
});

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

Las directivas se aplican mediante la propiedad Policy en el atributo [Authorize]:

[Authorize(Policy = "RequireAdministratorRole")]
public IActionResult Shutdown()
{
    return View();
}

Para especificar varios roles permitidos en un requisito, especifíquelos como parámetros para el método RequireRole:

var builder = WebApplication.CreateBuilder(args);

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

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("ElevatedRights", policy =>
          policy.RequireRole("Administrator", "PowerUser", "BackupAdministrator"));
});

var app = builder.Build();

El código anterior autoriza a los usuarios que pertenecen a los roles Administrator o PowerUserBackupAdministrator.

Cuando se crea una identidad, puede pertenecer a uno o varios roles. Por ejemplo, Tracy puede pertenecer a los roles de administrador y usuario, mientras que Scott solo puede pertenecer al rol usuario. La forma en que se crean y administran estos roles depende de la memoria auxiliar del proceso de autorización. Los roles se exponen al desarrollador a través del método IsInRole en la clase ClaimsPrincipal.

Se recomienda no usar roles como notificaciones, sino usar notificaciones. Al usar aplicaciones de página única (SPA), consulte Uso de Identity para proteger un back-end de API web para SPA.

Adición de comprobaciones de roles

Comprobaciones de autorización basadas en roles:

  • Son declarativas.
  • Se aplican a Páginas de Razor, controladores o acciones dentro de un controlador.
  • No se pueden aplicar en el nivel de controlador de Razor Page, deben aplicarse a la página.

Las comprobaciones de autorización basada en roles especifican los roles de los que el usuario actual debe ser miembro para acceder al recurso solicitado.

Por ejemplo, el código siguiente limita el acceso a las acciones de AdministrationController para los usuarios que son miembros del rol Administrator:

[Authorize(Roles = "Administrator")]
public class AdministrationController : Controller
{
    public IActionResult Index() =>
        Content("Administrator");
}

Se pueden especificar varios roles como una lista separada por comas:

[Authorize(Roles = "HRManager,Finance")]
public class SalaryController : Controller
{
    public IActionResult Payslip() =>
                    Content("HRManager || Finance");
}

El controlador SalaryController solo es accesible para los usuarios que son miembros del rol HRManagero del rol Finance.

Si aplica varios atributos, un usuario que acceda deberá ser miembro de todos los roles especificados. En el ejemplo siguiente se requiere que un usuario sea miembro del rol PowerUser y ControlPanelUser:

[Authorize(Roles = "PowerUser")]
[Authorize(Roles = "ControlPanelUser")]
public class ControlPanelController : Controller
{
    public IActionResult Index() =>
        Content("PowerUser && ControlPanelUser");
}

Puede limitar aún más el acceso aplicando atributos de autorización de roles adicionales en el nivel de acción:

[Authorize(Roles = "Administrator, PowerUser")]
public class ControlAllPanelController : Controller
{
    public IActionResult SetTime() =>
        Content("Administrator || PowerUser");

    [Authorize(Roles = "Administrator")]
    public IActionResult ShutDown() =>
        Content("Administrator only");
}

Si se aplican varios atributos en los niveles de controlador y acción, todos los atributos deben pasar antes de que se conceda acceso:

[Authorize(Roles = "Administrator")]
public class ControlAllPanelController2 : Controller
{
    public IActionResult SetTime() =>
        Content("Administrator only");

    [Authorize(Roles = "PowerUser")]
    public IActionResult ShutDown() =>
        Content("Administrator && PowerUser");
}

En el controlador ControlAllPanelController anterior:

  • Los miembros del rol Administrator o del rol PowerUser pueden acceder al controlador y a la acción SetTime.
  • Solo los miembros del rol Administrator pueden acceder a la acción ShutDown.

También puede bloquear un controlador, pero permitir el acceso anónimo y no autenticado a acciones individuales.

[Authorize]
public class Control3PanelController : Controller
{
    public IActionResult SetTime() =>
        Content("[Authorize]");

    [AllowAnonymous]
    public IActionResult Login() =>
        Content("[AllowAnonymous]");
}

Para Razor Pages, la [Authorize] se puede aplicar mediante:

[Authorize(Policy = "RequireAdministratorRole")]
public class UpdateModel : PageModel
{
    public ActionResult OnPost()
    {
    }
}

Importante

Los atributos de filtro, incluidos AuthorizeAttribute, solo se pueden aplicar a PageModel y no se pueden aplicar a métodos de controlador de página específicos.

Comprobaciones de roles basadas en directivas

Los requisitos de rol también se pueden expresar mediante la nueva sintaxis de directiva, donde un desarrollador registra una directiva en el inicio como parte de la configuración del servicio de autorización. Esto ocurre normalmente en ConfigureServices() en el archivo Startup.cs.

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

    services.AddAuthorization(options =>
    {
        options.AddPolicy("RequireAdministratorRole",
             policy => policy.RequireRole("Administrator"));
    });
}

Las directivas se aplican mediante la propiedad Policy en el atributo [Authorize]:

[Authorize(Policy = "RequireAdministratorRole")]
public IActionResult Shutdown()
{
    return View();
}

Si desea especificar varios roles permitidos en un requisito, puede especificarlos como parámetros para el método RequireRole:

options.AddPolicy("ElevatedRights", policy =>
                  policy.RequireRole("Administrator", "PowerUser", "BackupAdministrator"));

En este ejemplo se autoriza a los usuarios que pertenecen a los roles Administrator, PowerUser o BackupAdministrator.

Agregar servicios de rol a Identity

Anexe AddRoles para agregar servicios de rol:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));
    services.AddDefaultIdentity<IdentityUser>()
        .AddRoles<IdentityRole>()
        .AddEntityFrameworkStores<ApplicationDbContext>();

    services.AddControllersWithViews();
    services.AddRazorPages();
}