新文章 网摘 文章 随笔 日记

受保护的 Web API:验证作用域和应用角色

本文介绍如何向 Web API 添加授权。此保护可确保 API 仅由以下人员调用:

  • 代表具有正确作用域和角色的用户的应用程序。
  • 具有正确应用程序角色的守护程序应用。

本文中的代码段摘自 GitHub 上的以下代码示例:

若要保护 ASP.NET 或 ASP.NET 核心 Web API,必须将属性添加到以下项之一:[Authorize]

  • 控制器本身(如果希望保护所有控制器操作)
  • API 的单个控制器操作
C#
    [Authorize]
    public class TodoListController : Controller
    {
     // ...
    }

但这种保护是不够的。它仅保证 ASP.NET 和 ASP.NET 核心验证令牌。您的 API 需要验证用于调用 API 的令牌是否使用预期的声明进行请求。这些声明尤其需要验证:

  • 如果代表用户调用 API,则为作用域
  • 应用角色(如果可以从守护程序应用调用 API)。

验证代表用户调用的 API 中的作用域

如果客户端应用代表用户调用 API,则 API 需要请求具有 API 特定作用域的持有者令牌。有关更多信息,请参见代码配置|持有者令牌

在 ASP.NET 核心中,可以使用每个控制器操作中的作用域来验证作用域。您还可以在控制器级别或整个应用程序进行验证。

验证每个控制器操作上的作用域

您可以使用 属性验证控制器操作中的作用域。此属性具有多个覆盖。一个直接获取所需作用域,另一个采用配置密钥。[RequiredScope]

使用硬编码的作用域验证控制器操作上的作用域

以下代码段显示了该属性在硬编码作用域中的用法。[RequiredScope]

C#
using Microsoft.Identity.Web

[Authorize]
public class TodoListController : Controller
{
    /// <summary>
    /// The web API will accept only tokens that have the `access_as_user` scope for
    /// this API.
    /// </summary>
    const string scopeRequiredByApi = "access_as_user";

    // GET: api/values
    [HttpGet]
    [RequiredScope(scopeRequiredByApi)]
    public IEnumerable<TodoItem> Get()
    {
        // Do the work and return the result.
        // ...
    }
 // ...
}
使用配置中定义的作用域验证控制器操作上的作用域

您还可以在配置中声明这些必需的作用域,并引用配置键:

例如,如果在 appsettings.json 中,您具有以下配置:

金俊杰
{
 "AzureAd" : {
   // more settings
   "Scopes" : "access_as_user access_as_admin"
  }
}

然后,在属性中引用它:[RequiredScope]

C#
using Microsoft.Identity.Web

[Authorize]
public class TodoListController : Controller
{
    // GET: api/values
    [HttpGet]
    [RequiredScope(RequiredScopesConfigurationKey = "AzureAd:Scopes")]
    public IEnumerable<TodoItem> Get()
    {
        // Do the work and return the result.
        // ...
    }
 // ...
}
有条件地验证范围

在某些情况下,您希望有条件地验证范围。您可以使用 上的扩展方法来执行此操作。VerifyUserHasAnyAcceptedScopeHttpContext

C#
using Microsoft.Identity.Web

[Authorize]
public class TodoListController : Controller
{
    /// <summary>
    /// The web API will accept only tokens 1) for users, 2) that have the `access_as_user` scope for
    /// this API.
    /// </summary>
    static readonly string[] scopeRequiredByApi = new string[] { "access_as_user" };

    // GET: api/values
    [HttpGet]
    public IEnumerable<TodoItem> Get()
    {
         HttpContext.VerifyUserHasAnyAcceptedScope(scopeRequiredByApi);
        // Do the work and return the result.
        // ...
    }
 // ...
}

在控制器级别验证作用域

您还可以验证整个控制器的作用域

验证具有硬编码作用域的控制器上的作用域

以下代码段显示了该属性在控制器上具有硬编码作用域的用法。[RequiredScope]

C#
using Microsoft.Identity.Web

[Authorize]
[RequiredScope(scopeRequiredByApi)]
public class TodoListController : Controller
{
    /// <summary>
    /// The web API will accept only tokens 1) for users, 2) that have the `access_as_user` scope for
    /// this API.
    /// </summary>
    static readonly string[] scopeRequiredByApi = new string[] { "access_as_user" };

    // GET: api/values
    [HttpGet]
    public IEnumerable<TodoItem> Get()
    {
        // Do the work and return the result.
        // ...
    }
 // ...
}
使用配置中定义的作用域验证控制器上的作用域

与 on action 一样,您也可以在配置中声明这些必需的作用域,并引用配置键:

C#
using Microsoft.Identity.Web

[Authorize]
[RequiredScope(RequiredScopesConfigurationKey = "AzureAd:Scopes")
public class TodoListController : Controller
{
    // GET: api/values
    [HttpGet]
    public IEnumerable<TodoItem> Get()
    {
        // Do the work and return the result.
        // ...
    }
 // ...
}

更全面地验证范围

建议的方法为 Web API 定义粒度作用域并验证每个控制器操作中的作用域。但是,也可以在应用程序或控制器级别验证作用域。有关详细信息,请参阅 ASP.NET 核心文档中的基于声明的授权

验证了什么?

属性和方法执行类似于以下步骤的操作:[RequiredScope]VerifyUserHasAnyAcceptedScope

  • 验证是否存在名为 或 的声明。http://schemas.microsoft.com/identity/claims/scopescp
  • 验证声明是否具有包含 API 预期的范围的值。

验证守护程序应用调用的 API 中的应用角色

如果 Web API 由守护程序应用调用,则该应用应要求应用程序对 Web API 具有权限。如公开应用程序权限(应用程序角色)中所示,您的 API 会公开此类权限。一个示例是应用角色。access_as_application

现在需要让 API 验证它收到的令牌是否包含声明,以及此声明是否具有预期值。验证代码类似于验证委派权限的代码,不同之处在于控制器操作测试角色而不是作用域:roles

以下代码段演示如何验证应用程序角色

C#
using Microsoft.Identity.Web

[Authorize]
public class TodoListController : ApiController
{
    public IEnumerable<TodoItem> Get()
    {
        HttpContext.ValidateAppRole("access_as_application");
        // ...
    }

相反,您可以在控制器或操作(或剃须刀页面)上使用 [Authorize(角色 = “access_as_application”)]属性。

断续器
[Authorize(Roles = "access_as_application")]
MyController : ApiController
{
    // ...
}

ASP.NET Core 中基于角色的授权列出了几种实现基于角色的授权的方法。开发人员可以从中选择一个适合他们各自场景的选择。

有关工作示例,请参阅有关按角色和组授权的 Web 应用增量教程。

验证代表用户调用的 API 中的应用角色

用户还可以在用户分配模式中使用角色声明,如如何在应用程序中添加应用角色并在令牌中接收它们中所示。如果角色可分配给两者,则选中角色将允许应用以用户身份登录,用户以应用身份登录。我们建议你为用户和应用声明不同的角色,以防止这种混淆。

如果已使用用户/组定义了应用角色,则还可以在 API 中验证角色声明以及作用域。此方案中应用角色的验证逻辑与守护程序应用调用 API 时的验证逻辑相同,因为用户/组和应用程序的角色声明中没有区别。

如果 Web API 仅应由守护程序应用程序调用,则接受仅限应用程序的令牌

如果只希望守护程序应用调用 Web API,请在验证应用角色时添加令牌为仅限应用令牌的条件。

C#
string oid = ClaimsPrincipal.Current.FindFirst("oid")?.Value;
string sub = ClaimsPrincipal.Current.FindFirst("sub")?.Value;
bool isAppOnly = oid != null && sub != null && oid == sub;

检查相反条件仅允许登录用户的应用调用你的 API。

使用基于 ACL 的授权

或者,对于基于应用角色的授权,可以使用基于访问控制列表 (ACL) 的授权模式来保护 Web API,以控制没有角色声明的令牌

如果您在 ASP.NET 核心上使用 Microsoft.Identity.Web,则需要声明您使用的是基于 ACL 的授权,否则,当角色和作用域都不在提供的“声明”中时,微软标识 Web 将引发异常:

发短信
System.UnauthorizedAccessException: IDW10201: Neither scope or roles claim was found in the bearer token.

若要避免此异常,请在 appsettings.json 中或以编程方式将配置属性设置为 true。AllowWebApiToBeAuthorizedByACL

Json
{
 "AzureAD"
 {
  // other properties
  "AllowWebApiToBeAuthorizedByACL" : true,
  // other properties
 }
}

如果设置为 true,则负责确保 ACL 机制。AllowWebApiToBeAuthorizedByACL

四. 今后的步骤

请转到此方案中的下一篇文章,移动到生产环境

 

验证作用域和应用角色受保护的 Web API - Microsoft Entra |微软学习

posted @ 2022-10-22 10:56  岭南春  阅读(86)  评论(0)    收藏  举报