ASP.NET Core 中基于角色的授权

创建 identity 时,它可能属于一个或多个角色。 例如,Tracy 可能属于 AdministratorUser 角色,而 Scott 只属于 User 角色。 如何创建和管理这些角色取决于授权过程的后备存储。 角色通过 ClaimsPrincipal 类的 IsInRole 方法向开发人员公开。 AddRoles 必须添加到角色服务。

虽然角色是声明,但并非所有声明都是角色。 根据标识颁发者的不同,角色可能是一组用户,可以为组成员应用声明,以及对标识的实际声明。 但是,声明是关于个人用户的信息。 使用角色向用户添加声明可能会混淆用户与其个人声明之间的界限。 这种混淆就是为什么 SPA 模板不是围绕角色设计的原因。 此外,对于从本地旧系统迁移的组织而言,多年来角色的激增可能意味着角色声明可能太大而无法包含在 SPA 可用的令牌中。 若要保护 SPA,请参阅使用 Identity 保护 SPA 的 Web API 后端

将角色服务添加到 Identity

通过使用应用 Identity 配置中的角色类型调用 AddRoles ,在 Program.cs 中注册基于角色的授权服务。 以下示例中的角色类型为 IdentityRole

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

上述代码需要 Microsoft.AspNetCore.Identity.UI 包和 Microsoft.AspNetCore.Identityusing 指令。

添加角色检查

基于角色的授权检查:

  • 是声明性的,指定当前用户必须是其成员才能访问请求的资源的角色。
  • 适用于控制器中的 Razor 页面、控制器或操作。
  • 不能在 Razor 页面处理程序级别应用,它们必须应用于页面

例如,以下代码将访问权限限制为属于 Administrator 角色成员的用户对 AdministrationController 的任何操作:

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

多个角色可以指定为逗号分隔的列表:

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

仅当用户为 HRManagerFinance 角色成员时才可访问 SalaryController

应用多个属性时,访问用户必须是所有指定角色的成员。 以下示例要求用户必须同时是 PowerUserControlPanelUser 角色的成员

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

可以通过在操作级别应用其他角色授权属性来限制对操作的访问:

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

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

前面的 ControlAllPanelController 控制器中:

  • Administrator 角色或 PowerUser 角色的成员可以访问控制器和 SetTime 操作。
  • 只有 Administrator 角色的成员才能访问 ShutDown 操作。

可以保护控制器,但允许对单个操作的匿名、未经身份验证的访问:

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

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

对于 Razor 页面,可以通过以下任一方法应用 [Authorize]

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

重要

筛选器属性(包括 AuthorizeAttribute),只能应用于 PageModel,不能应用于特定页面处理程序方法。

基于 Policy 的角色检查

角色要求也可以使用 Policy 语法来表达,其中开发人员在应用程序启动时注册策略作为授权服务配置的一部分。 这通常出现在 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();

使用 [Authorize] 属性上的 Policy 属性应用策略:

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

若要在某个要求中指定多个允许的角色,请将它们指定为 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();

前面的代码授权属于 AdministratorPowerUserBackupAdministrator 角色的用户。

创建 identity 时,它可能属于一个或多个角色。 例如,Tracy 可能属于管理员和用户角色,而 Scott 可能只属于用户角色。 如何创建和管理这些角色取决于授权过程的后备存储。 角色通过 ClaimsPrincipal 类的 IsInRole 方法向开发人员公开。

我们建议不要将角色用作声明,而是使用声明。 使用单页应用 (SPA) 时,请参阅使用 Identity 保护 SPA 的 Web API 后端

添加角色检查

基于角色的授权检查:

  • 是声明性的。
  • 适用于控制器中的 Razor 页面、控制器或操作。
  • 不能在 Razor 页面处理程序级别应用,它们必须应用于页面

基于角色的授权检查指定当前用户必须是哪些角色才能访问所请求的资源。

例如,以下代码将访问权限限制为属于 Administrator 角色成员的用户对 AdministrationController 的任何操作:

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

多个角色可以指定为逗号分隔的列表:

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

仅当用户为 HRManagerFinance 角色成员时才可访问控制器 SalaryController

如果应用多个属性,则访问用户必须是所有指定角色的成员。 以下示例要求用户必须是 PowerUserControlPanelUser 角色的成员:

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

可以通过在操作级别应用其他角色授权属性来进一步限制访问:

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

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

如果在控制器和操作级别应用了多个特性,则在授予访问权限之前必须通过所有特性:

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

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

前面的 ControlAllPanelController 控制器中:

  • Administrator 角色或 PowerUser 角色的成员可以访问控制器和 SetTime 操作。
  • 只有 Administrator 角色的成员才能访问 ShutDown 操作。

还可以锁定控制器,但允许匿名、未经身份验证的访问单个操作。

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

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

对于 Razor 页面,可以通过以下任一方法来应用 [Authorize]

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

重要

筛选器属性(包括 AuthorizeAttribute),只能应用于 PageModel,不能应用于特定页面处理程序方法。

基于 Policy 的角色检查

角色要求也可以使用新的 Policy 语法来表达,其中开发人员在启动时注册策略作为授权服务配置的一部分。 这通常出现在 Startup.cs 文件的 ConfigureServices() 中。

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

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

使用 [Authorize] 属性上的 Policy 属性应用策略:

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

若要在某个要求中指定多个允许的角色,则可将它们指定为 RequireRole 方法的参数:

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

此示例授权属于 AdministratorPowerUserBackupAdministrator 角色的用户。

添加角色服务到 Identity

附加 AddRoles 以添加角色服务:

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