关于 .NET 微服务和 Web 应用程序中的授权

小窍门

此内容摘自电子书《适用于容器化 .NET 应用程序的 .NET 微服务体系结构》,可以在 .NET Docs 上获取,也可以下载免费的 PDF 以供离线阅读。

适用于容器化 .NET 应用程序的 .NET 微服务体系结构电子书封面缩略图。

身份验证后,ASP.NET 核心 Web API 需要授权访问。 此过程可让服务将 API 提供给某些经过身份验证的用户使用,而不是提供给所有用户使用。 身份验证可基于用户角色或自定义策略完成,这可能包括检查声明或试探法。

限制对 ASP.NET Core MVC 路由的访问,就像将 Authorize 属性应用于操作方法一样简单(如果所有控制器的操作都需要授权),如以下示例所示:

public class AccountController : Controller
{
    public ActionResult Login()
    {
    }

    [Authorize]
    public ActionResult Logout()
    {
    }
}

默认情况下,添加不带参数的 Authorize 属性将限制对该控制器或作的经过身份验证的用户的访问。 若要进一步限制仅针对特定用户可用的 API,可以扩展该属性以指定用户必须满足的必需角色或策略。

实现基于角色的授权

ASP.NET 核心标识具有角色的内置概念。 除了用户之外,ASP.NET Core Identity 还会存储应用程序使用的不同角色的信息,并跟踪分配给哪些角色的用户。 可以使用更新持久存储中的角色的 RoleManager 类型和可以授予或撤销用户角色的 UserManager 类型以编程方式更改这些分配。

如果使用 JWT 持有者令牌进行身份验证,ASP.NET Core JWT 持有者身份验证中间件将基于令牌中找到的角色声明填充用户的角色。 若要将 MVC作或控制器的访问权限限制为特定角色中的用户,可以在授权批注(属性)中包含 Roles 参数,如以下代码片段所示:

[Authorize(Roles = "Administrator, PowerUser")]
public class ControlPanelController : Controller
{
    public ActionResult SetTime()
    {
    }

    [Authorize(Roles = "Administrator")]
    public ActionResult ShutDown()
    {
    }
}

在此示例中,只有管理员或 PowerUser 角色中的用户才能访问 ControlPanel 控制器中的 API(例如执行 SetTime作)。 关闭 API 的限制进一步加强,仅允许管理员角色的用户访问。

若要要求用户具有多个角色,请使用多个 Authorize 属性,如以下示例所示:

[Authorize(Roles = "Administrator, PowerUser")]
[Authorize(Roles = "RemoteEmployee ")]
[Authorize(Policy = "CustomPolicy")]
public ActionResult API1 ()
{
}

在此示例中,若要调用 API1,用户必须:

  • 处于 Adminstrator 或 PowerUser 角色;以及

  • 处于 RemoteEmployee 角色;

  • 满足 CustomPolicy 授权的自定义处理程序。

实现基于策略的授权

还可以使用 授权策略编写自定义授权规则。 本部分提供概述。 有关详细信息,请参阅 ASP.NET 授权研讨会

Startup.ConfigureServices 方法中使用 service.AddAuthorization 方法注册了自定义授权策略。 此方法采用了配置 AuthorizationOptions 参数的委托。

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

    options.AddPolicy("EmployeesOnly", policy =>
        policy.RequireClaim("EmployeeNumber"));

    options.AddPolicy("Over21", policy =>
        policy.Requirements.Add(new MinimumAgeRequirement(21)));
});

如示例中所示,策略可以与不同类型的要求相关联。 注册策略后,可通过将策略名称作为 Authorize 特性的 Policy 参数传递,将这些策略应用于操作或控制器(例如,[Authorize(Policy="EmployeesOnly")]),策略可有多个要求,而非仅有一个(如这些示例所示)。

在前面的示例中,第一个 AddPolicy 调用只是按角色授权的替代方法。 如果 [Authorize(Policy="AdministratorsOnly")] 应用于 API,则只有管理员角色中的用户才能访问它。

第二个 AddPolicy 调用演示要求应该为用户提供特定声明所使用的一种简单方法。 RequireClaim 方法还可选择为声明采用期望值。 如果指定了值,则仅当用户具有正确类型和指定值之一的声明时,才满足要求。 如果使用 JWT 持有者身份验证中间件,所有 JWT 属性都将作为用户声明提供。

此处显示的最有趣的策略是第三 AddPolicy 种方法,因为它使用自定义授权要求。 通过使用自定义授权要求,可以完全控制授权的执行方式。 为此,必须实现这些类型:

如果用户满足要求,则调用 context.Succeed 将指示用户获得授权。 如果用户可以通过多种方式满足授权要求,则可以创建多个处理程序。

除使用 AddPolicy 调用注册自定义策略要求外,还需要通过依赖项注入注册自定义要求处理程序 (services.AddTransient<IAuthorizationHandler, MinimumAgeHandler>())。

ASP.NET 核心DateOfBirth提供了用于检查用户年龄(基于声明)的自定义授权要求和处理程序示例。

授权和最小 API

ASP.NET 支持最小 API 作为基于控制器的 API 的替代方法。 授权策略是为最小 API 配置授权的建议方法,如以下示例所示:

// Program.cs
builder.Services.AddAuthorizationBuilder()
  .AddPolicy("admin_greetings", policy =>
        policy
            .RequireRole("admin")
            .RequireScope("greetings_api"));

// build the app

app.MapGet("/hello", () => "Hello world!")
  .RequireAuthorization("admin_greetings");

其他资源