受保护的 Web API:验证作用域和应用角色
本文介绍如何向 Web API 添加授权。此保护可确保 API 仅由以下人员调用:
- 代表具有正确作用域和角色的用户的应用程序。
- 具有正确应用程序角色的守护程序应用。
本文中的代码段摘自 GitHub 上的以下代码示例:
若要保护 ASP.NET 或 ASP.NET 核心 Web API,必须将属性添加到以下项之一:[Authorize]
- 控制器本身(如果希望保护所有控制器操作)
- API 的单个控制器操作
[Authorize]
public class TodoListController : Controller
{
// ...
}
但这种保护是不够的。它仅保证 ASP.NET 和 ASP.NET 核心验证令牌。您的 API 需要验证用于调用 API 的令牌是否使用预期的声明进行请求。这些声明尤其需要验证:
- 如果代表用户调用 API,则为作用域。
- 应用角色(如果可以从守护程序应用调用 API)。
验证代表用户调用的 API 中的作用域
如果客户端应用代表用户调用 API,则 API 需要请求具有 API 特定作用域的持有者令牌。有关更多信息,请参见代码配置|持有者令牌。
在 ASP.NET 核心中,可以使用每个控制器操作中的作用域来验证作用域。您还可以在控制器级别或整个应用程序进行验证。
验证每个控制器操作上的作用域
您可以使用 属性验证控制器操作中的作用域。此属性具有多个覆盖。一个直接获取所需作用域,另一个采用配置密钥。[RequiredScope]
使用硬编码的作用域验证控制器操作上的作用域
以下代码段显示了该属性在硬编码作用域中的用法。[RequiredScope]
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]
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
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]
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 一样,您也可以在配置中声明这些必需的作用域,并引用配置键:
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
以下代码段演示如何验证应用程序角色
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,请在验证应用角色时添加令牌为仅限应用令牌的条件。
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
{
"AzureAD"
{
// other properties
"AllowWebApiToBeAuthorizedByACL" : true,
// other properties
}
}
如果设置为 true,则负责确保 ACL 机制。AllowWebApiToBeAuthorizedByACL
四. 今后的步骤
请转到此方案中的下一篇文章,移动到生产环境。
浙公网安备 33010602011771号