ASP.NET Core 中基于声明的授权

创建标识后,可为其分配一个或多个由受信任方颁发的声明。 声明是一个名称值对,表示使用者是什么,而不是使用者可以做什么。 例如,你可能拥有一个由当地驾驶许可证颁发机构颁发的驾照。 驾照上有你的出生日期。 在这种情况下,声明名称为 DateOfBirth,声明值将是你的出生日期(例如 8th June 1970),颁发者为驾驶许可证颁发机构。 基于声明的授权,在最简单的情况下,检查声明的值并允许基于该值访问资源。 例如,如果你想进入夜总会,授权过程可能是:

在允许你进入之前,门禁安全管理人员将评估你的出生日期声明的值以及他们是否信任颁发者(驾驶许可证颁发机构)。

一个标识可以包含多个具有多个值的声明,并且可以包含多个相同类型的声明。

添加声明检查

基于声明的授权检查:

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

代码中的声明指定当前用户必须拥有的声明,以及声明必须持有的值(可选)才能访问所请求的资源。 声明要求是基于策略的,开发人员必须构建并注册一个表达声明要求的策略。

最简单的声明策略类型会查找是否存在声明,且不会对值进行检查。

构建并注册策略,然后调用 UseAuthorization。 注册策略将在授权服务配置过程中进行,通常在 Program.cs 文件中进行:

var builder = WebApplication.CreateBuilder(args);

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

builder.Services.AddAuthorization(options =>
{
   options.AddPolicy("EmployeeOnly", policy => policy.RequireClaim("EmployeeNumber"));
});

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

在这种情况下,EmployeeOnly 策略会检查当前标识上是否存在 EmployeeNumber 声明。

使用 [Authorize] 特性上的 Policy 属性应用策略,以指定策略名称。

[Authorize(Policy = "EmployeeOnly")]
public IActionResult VacationBalance()
{
    return View();
}

[Authorize] 属性可应用于整个控制器或 Razor Pages,在此实例中,只允许与策略匹配的标识访问控制器上的任何操作。

[Authorize(Policy = "EmployeeOnly")]
public class VacationController : Controller
{
    public IActionResult Index()
    {
        return View();
    }

    public ActionResult VacationBalance()
    {
        return View();
    }

    [AllowAnonymous]
    public ActionResult VacationPolicy()
    {
        return View();
    }
}

以下代码会将 [Authorize] 属性应用于 Razor Pages:

[Authorize(Policy = "EmployeeOnly")]
public class IndexModel : PageModel
{
    public void OnGet()
    {

    }
}

不能在 Razor Pages 处理程序级别上应用策略,而是必须将其应用于该 Pages。

如果有受 [Authorize] 属性保护的控制器,但希望允许匿名访问特定操作,请应用 AllowAnonymousAttribute 属性。

[Authorize(Policy = "EmployeeOnly")]
public class VacationController : Controller
{
    public IActionResult Index()
    {
        return View();
    }

    public ActionResult VacationBalance()
    {
        return View();
    }

    [AllowAnonymous]
    public ActionResult VacationPolicy()
    {
        return View();
    }
}

由于策略不能应用于 Razor Pages 处理程序级别,因此,如果必须在该 Pages 处理程序级别应用策略,建议你使用控制器。 应用的其余部分如何不需要 Razor Pages 处理程序级别的策略,则可使用 Razor Pages。

大多数声明都具有值。 可在创建策略时指定允许值的列表。 以下示例仅适用于员工编号为 1、2、3、4 或 5 的员工。

var builder = WebApplication.CreateBuilder(args);

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

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("Founders", policy =>
                      policy.RequireClaim("EmployeeNumber", "1", "2", "3", "4", "5"));
});

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

添加通用声明检查

如果声明值不是单个值或需要转换,请使用 RequireAssertion。 有关详细信息,请参阅使用 func 来实现策略

多重策略评估

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

[Authorize(Policy = "EmployeeOnly")]
public class SalaryController : Controller
{
    public IActionResult Index()
    {
        return View();
    }

    public IActionResult Payslip()
    {
        return View();
    }

    [Authorize(Policy = "HumanResources")]
    public IActionResult UpdateSalary()
    {
        return View();
    }
}

在前面的示例中,满足 EmployeeOnly 策略的任何标识都可访问该 Payslip 操作,因为该策略是在控制器上强制实施的。 但是,若要调用 UpdateSalary 操作,标识必须满足 EmployeeOnly 策略和 HumanResources 策略

如果需要更复杂的策略(例如获取出生日期声明、基于此信息计算年龄然后检查年龄是否为 21 岁或以上),则需要编写自定义策略处理程序

在下面的示例中,两个页面处理程序方法必须同时满足 EmployeeOnly 策略和 HumanResources 策略:

[Authorize(Policy = "EmployeeOnly")]
[Authorize(Policy = "HumanResources")]
public class SalaryModel : PageModel
{
    public ContentResult OnGetPayStub()
    {
        return Content("OnGetPayStub");
    }

    public ContentResult OnGetSalary()
    {
        return Content("OnGetSalary");
    }
}

创建标识后,可为其分配一个或多个由受信任方颁发的声明。 声明是一个名称值对,表示使用者是什么,而不是使用者可以做什么。 例如,你可能拥有一个由当地驾驶许可证颁发机构颁发的驾照。 驾照上有你的出生日期。 在这种情况下,声明名称为 DateOfBirth,声明值将是你的出生日期(例如 8th June 1970),颁发者为驾驶许可证颁发机构。 基于声明的授权,在最简单的情况下,检查声明的值并允许基于该值访问资源。 例如,如果你想进入夜总会,授权过程可能是:

在允许你进入之前,门禁安全管理人员将评估你的出生日期声明的值以及他们是否信任颁发者(驾驶许可证颁发机构)。

一个标识可以包含多个具有多个值的声明,并且可以包含多个相同类型的声明。

添加声明检查

基于声明的授权检查是声明性的 - 开发人员将其嵌入到他们的代码中,针对控制器或控制器中的操作,指定当前用户必须拥有的声明,以及声明必须持有的值(可选)以访问所请求的资源。 声明要求是基于策略的,开发人员必须构建并注册一个表达声明要求的策略。

最简单的声明策略类型会查找是否存在声明,且不会对值进行检查。

构建并注册策略。 此操作在授权服务配置期间进行,通常在 Startup.cs 文件中的 ConfigureServices() 中进行。

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

    services.AddAuthorization(options =>
    {
        options.AddPolicy("EmployeeOnly", policy => policy.RequireClaim("EmployeeNumber"));
    });
}

Configure 中调用 UseAuthorization。 以下代码由 ASP.NET Core Web 应用模板生成:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseMigrationsEndPoint();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthentication();
    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

在这种情况下,EmployeeOnly 策略会检查当前标识上是否存在 EmployeeNumber 声明。

然后使用 [Authorize] 属性上的 Policy 属性应用策略,以指定策略名称;

[Authorize(Policy = "EmployeeOnly")]
public IActionResult VacationBalance()
{
    return View();
}

[Authorize] 属性可应用于整个控制器,在此实例中,将仅允许与策略匹配的标识访问控制器上的任何操作。

[Authorize(Policy = "EmployeeOnly")]
public class VacationController : Controller
{
    public ActionResult VacationBalance()
    {
    }
}

如果有受 [Authorize] 属性保护的控制器,但希望允许匿名访问特定操作,请应用 AllowAnonymousAttribute 属性。

[Authorize(Policy = "EmployeeOnly")]
public class VacationController : Controller
{
    public ActionResult VacationBalance()
    {
    }

    [AllowAnonymous]
    public ActionResult VacationPolicy()
    {
    }
}

大多数声明都具有值。 可在创建策略时指定允许值的列表。 以下示例仅适用于员工编号为 1、2、3、4 或 5 的员工。

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

    services.AddAuthorization(options =>
    {
        options.AddPolicy("Founders", policy =>
                          policy.RequireClaim("EmployeeNumber", "1", "2", "3", "4", "5"));
    });
}

添加通用声明检查

如果声明值不是单个值或需要转换,请使用 RequireAssertion。 有关详细信息,请参阅使用 func 来实现策略

多重策略评估

如果将多个策略应用于控制器或操作,则必须通过所有策略才能授予访问权限。 例如:

[Authorize(Policy = "EmployeeOnly")]
public class SalaryController : Controller
{
    public ActionResult Payslip()
    {
    }

    [Authorize(Policy = "HumanResources")]
    public ActionResult UpdateSalary()
    {
    }
}

在上面的示例中,满足 EmployeeOnly 策略的任何标识都可访问该 Payslip 操作,因为该策略是在控制器上强制实施的。 但是,若要调用 UpdateSalary 操作,标识必须满足 EmployeeOnly 策略和 HumanResources 策略。

如果需要更复杂的策略(例如获取出生日期声明、基于此信息计算年龄然后检查年龄是否为 21 岁或以上),则需要编写自定义策略处理程序

创建标识后,可为其分配一个或多个由受信任方颁发的声明。 声明是一个名称值对,表示使用者是什么,而不是使用者可以做什么。 例如,你可能拥有一个由当地驾驶许可证颁发机构颁发的驾照。 驾照上有你的出生日期。 在这种情况下,声明名称为 DateOfBirth,声明值将是你的出生日期(例如 8th June 1970),颁发者为驾驶许可证颁发机构。 基于声明的授权,在最简单的情况下,检查声明的值并允许基于该值访问资源。 例如,如果你想进入夜总会,授权过程可能是:

在允许你进入之前,门禁安全管理人员将评估你的出生日期声明的值以及他们是否信任颁发者(驾驶许可证颁发机构)。

一个标识可以包含多个具有多个值的声明,并且可以包含多个相同类型的声明。

添加声明检查

基于声明的授权检查是声明性的 - 开发人员将其嵌入到他们的代码中,针对控制器或控制器中的操作,指定当前用户必须拥有的声明,以及声明必须持有的值(可选)以访问所请求的资源。 声明要求是基于策略的,开发人员必须构建并注册一个表达声明要求的策略。

最简单的声明策略类型会查找是否存在声明,且不会对值进行检查。

构建并注册策略。 此操作在授权服务配置期间进行,通常在 Startup.cs 文件中的 ConfigureServices() 中进行。

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

    services.AddAuthorization(options =>
    {
        options.AddPolicy("EmployeeOnly", policy => policy.RequireClaim("EmployeeNumber"));
    });
}

在这种情况下,EmployeeOnly 策略会检查当前标识上是否存在 EmployeeNumber 声明。

然后使用 [Authorize] 属性上的 Policy 属性应用策略,以指定策略名称;

[Authorize(Policy = "EmployeeOnly")]
public IActionResult VacationBalance()
{
    return View();
}

[Authorize] 属性可应用于整个控制器,在此实例中,将仅允许与策略匹配的标识访问控制器上的任何操作。

[Authorize(Policy = "EmployeeOnly")]
public class VacationController : Controller
{
    public ActionResult VacationBalance()
    {
    }
}

如果有受 [Authorize] 属性保护的控制器,但希望允许匿名访问特定操作,请应用 AllowAnonymousAttribute 属性。

[Authorize(Policy = "EmployeeOnly")]
public class VacationController : Controller
{
    public ActionResult VacationBalance()
    {
    }

    [AllowAnonymous]
    public ActionResult VacationPolicy()
    {
    }
}

大多数声明都具有值。 可在创建策略时指定允许值的列表。 以下示例仅适用于员工编号为 1、2、3、4 或 5 的员工。

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

    services.AddAuthorization(options =>
    {
        options.AddPolicy("Founders", policy =>
                          policy.RequireClaim("EmployeeNumber", "1", "2", "3", "4", "5"));
    });
}

添加通用声明检查

如果声明值不是单个值或需要转换,请使用 RequireAssertion。 有关详细信息,请参阅使用 func 来实现策略

多重策略评估

如果将多个策略应用于控制器或操作,则必须通过所有策略才能授予访问权限。 例如:

[Authorize(Policy = "EmployeeOnly")]
public class SalaryController : Controller
{
    public ActionResult Payslip()
    {
    }

    [Authorize(Policy = "HumanResources")]
    public ActionResult UpdateSalary()
    {
    }
}

在上面的示例中,满足 EmployeeOnly 策略的任何标识都可访问该 Payslip 操作,因为该策略是在控制器上强制实施的。 但是,若要调用 UpdateSalary 操作,标识必须满足 EmployeeOnly 策略和 HumanResources 策略。

如果需要更复杂的策略(例如获取出生日期声明、基于此信息计算年龄然后检查年龄是否为 21 岁或以上),则需要编写自定义策略处理程序