调用 Web API 的 Web 应用:代码配置

上一篇文章中,你在 Microsoft Entra 中注册了应用程序。 本文将介绍如何配置应用程序代码,并修改 Web 应用,使其不仅能登录用户,而且现在还能调用 Web API。 你创建的应用程序使用 OAuth 2.0 授权代码流让用户登录。 此流有两个步骤:

  1. 请求获取授权代码。 此部分将私密对话和用户一起委派给 Microsoft 标识平台。 在此对话期间,用户登录,并同意使用 Web API。 当此私密对话成功结束时,Web 应用会在其重定向 URI 中收到一个授权代码。
  2. 通过兑换授权代码来请求获取 API 的访问令牌。

先决条件

支持 Web 应用的 Microsoft 库

以下 Microsoft 库支持 Web 应用:

语言/框架 项目
GitHub
获取
started
用户登录 访问 Web API 正式发布 (GA) 或
公共预览版1
.NET MSAL.NET Microsoft.Identity.Client 库无法为用户登录请求 ID令牌。 库可以为受保护的 Web API 请求访问令牌。 GA
.NET Microsoft.IdentityModel Microsoft.IdentityModel Library cannot request ID tokens for user sign-in.2 Library cannot request access tokens for protected web APIs.2 GA
ASP.NET Core Microsoft.Identity.Web Microsoft.Identity.Web 快速入门 库可以为用户登录请求 ID令牌。 库可以为受保护的 Web API 请求访问令牌。 GA
Java MSAL4J msal4j 快速入门 库可以为用户登录请求 ID令牌。 库可以为受保护的 Web API 请求访问令牌。 GA
Spring spring-cloud-azure-starter-active-directory spring-cloud-azure-starter-active-directory 教程 库可以为用户登录请求 ID令牌。 库可以为受保护的 Web API 请求访问令牌。 GA
Node.js MSAL Node msal-node 快速入门 库可以为用户登录请求 ID令牌。 库可以为受保护的 Web API 请求访问令牌。 GA
Python MSAL Python msal 库可以为用户登录请求 ID令牌。 库可以为受保护的 Web API 请求访问令牌。 GA
Python identity identity 快速入门 库可以为用户登录请求 ID令牌。 库可以为受保护的 Web API 请求访问令牌。 --

(1) 联机服务通用许可条款适用于公共预览版的库

(2)Microsoft.IdentityModel 库仅验证令牌,它不能请求 ID 或访问令牌。

选择你感兴趣的平台的选项卡:

客户端密码或客户端证书

鉴于 Web 应用现在调用下游的 Web API,请提供 appsettings.json 文件中客户端密码或客户端证书。 你还可以添加一个节来指定:

  • 下游 Web API 的 URL
  • 调用 API 所需的范围

在下面的示例中,GraphBeta 节指定了这些设置。

{
  "AzureAd": {
    "Instance": "https://login.partner.microsoftonline.cn/",
    "ClientId": "[Enter_the_Application_Id_Here]",
    "TenantId": "common",

   // To call an API
   "ClientCredentials": [
    {
      "SourceType": "ClientSecret",
      "ClientSecret":"[Enter_the_Client_Secret_Here]"
    }
  ]
 },
 "GraphBeta": {
    "BaseUrl": "https://microsoftgraph.chinacloudapi.cn/beta",
    "Scopes": ["https://microsoftgraph.chinacloudapi.cn/user.read"]
    }
}

注意

你可建议一组客户端凭据,其中包括无凭据解决方案,例如 Azure Kubernetes 的工作负载联合身份验证。 先前版本的 Microsoft.Identity.Web 在单个属性“ClientSecret”中(而非“ClientCredentials”中)表示客户端密码。 这仍然支持向后兼容,但你不能同时使用“ClientSecret”属性和“ClientCredentials”集合。

你可以提供客户端证书,而不是客户端密码。 以下代码片段演示如何使用存储在 Azure Key Vault 中的证书。

{
  "AzureAd": {
    "Instance": "https://login.partner.microsoftonline.cn/",
    "ClientId": "[Enter_the_Application_Id_Here]",
    "TenantId": "common",

   // To call an API
   "ClientCredentials": [
      {
        "SourceType": "KeyVault",
        "KeyVaultUrl": "https://msidentitywebsamples.vault.azure.cn",
        "KeyVaultCertificateName": "MicrosoftIdentitySamplesCert"
      }
   ]
  },
  "GraphBeta": {
    "BaseUrl": "https://microsoftgraph.chinacloudapi.cn/beta",
    "Scopes": ["https://microsoftgraph.chinacloudapi.cn/user.read"]
  }
}

警告

如果忘记将 Scopes 更改为数组,则当尝试使用 IDownstreamApi 时,范围会显示为 null,并且 IDownstreamApi 会尝试匿名(未经身份验证)调用下游 API,这会导致 401/unauthenticated

Microsoft.Identity.Web 提供了多种通过配置或代码描述证书的方法。 有关详细信息,请参阅 GitHub 上的 Microsoft.Identity.Web - 使用证书

修改 Startup.cs 文件

Web 应用需要获取下游 API 的令牌。 可通过在 .AddMicrosoftIdentityWebApp(Configuration) 后面添加 .EnableTokenAcquisitionToCallDownstreamApi() 行来指定它。 此行公开 IAuthorizationHeaderProvider 服务,可在控制器和页面操作中使用该服务。 不过,正如你将在以下两个选项中看到的那样,该操作可以更简单地完成。 还需要在 Startup.cs 中选择令牌缓存实现(例如 .AddInMemoryTokenCaches()):

using Microsoft.Identity.Web;

public class Startup
{
  // ...
  public void ConfigureServices(IServiceCollection services)
  {
  // ...
  services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
          .AddMicrosoftIdentityWebApp(Configuration, "AzureAd")
            .EnableTokenAcquisitionToCallDownstreamApi(new string[]{"https://microsoftgraph.chinacloudapi.cn/user.read" })
            .AddInMemoryTokenCaches();
   // ...
  }
  // ...
}

传递给 EnableTokenAcquisitionToCallDownstreamApi 的范围是可选项,使 Web 应用能够请求范围并在用户登录时向用户征求对这些范围的许可。 如果未指定范围,Microsoft.Identity.Web 会启用增量许可体验。

Microsoft.Identity.Web 提供了两种从 Web 应用调用 Web API 的机制,而不要求你获取令牌。 选择哪种方式取决于你是要调用 Microsoft Graph 还是调用另一个 API。

选项 1:调用 Microsoft Graph

如果要调用 Microsoft Graph,则可通过 Microsoft.Identity.Web 在 API 操作中直接使用 GraphServiceClient(由 Microsoft Graph SDK 公开)。 若要公开 Microsoft Graph,请执行以下操作:

  1. Microsoft.Identity.Web.GraphServiceClient NuGet 包添加到项目。

  2. 在 Startup.cs 文件的 .EnableTokenAcquisitionToCallDownstreamApi() 后面添加 .AddMicrosoftGraph().AddMicrosoftGraph() 具有多个重写。 使用将配置部分作为参数的重写,代码变为:

    using Microsoft.Identity.Web;
    
    public class Startup
    {
      // ...
      public void ConfigureServices(IServiceCollection services)
      {
      // ...
      services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
              .AddMicrosoftIdentityWebApp(Configuration, "AzureAd")
                .EnableTokenAcquisitionToCallDownstreamApi(new string[]{"https://microsoftgraph.chinacloudapi.cn/user.read" })
                   .AddMicrosoftGraph(Configuration.GetSection("GraphBeta"))
                .AddInMemoryTokenCaches();
       // ...
      }
      // ...
    }
    

选项 2:调用下游 Web API,而不是 Microsoft Graph

如果要调用 API 而不是 Microsoft Graph,可通过 Microsoft.Identity.Web 在 API 操作中使用 IDownstreamApi 接口。 若要使用此接口,请执行以下操作:

  1. Microsoft.Identity.Web.DownstreamApi NuGet 包添加到项目中。

  2. 在 Startup.cs 文件的 .EnableTokenAcquisitionToCallDownstreamApi() 后面添加 .AddDownstreamApi().AddDownstreamApi() 具有两个参数,如以下代码片段所示:

    • 服务 (API) 的名称,用于在控制器操作中引用相应的配置
    • 表示用于调用下游 Web API 的参数的配置部分。
    using Microsoft.Identity.Web;
    
    public class Startup
    {
      // ...
      public void ConfigureServices(IServiceCollection services)
      {
      // ...
      services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
              .AddMicrosoftIdentityWebApp(Configuration, "AzureAd")
                .EnableTokenAcquisitionToCallDownstreamApi(new string[]{"https://microsoftgraph.chinacloudapi.cn/user.read" })
                   .AddDownstreamApi("MyApi", Configuration.GetSection("GraphBeta"))
                .AddInMemoryTokenCaches();
       // ...
      }
      // ...
    }
    

总结

与 Web API 一样,你可以选择各种令牌缓存实现。 有关详细信息,请参阅 GitHub 上的 Microsoft.Identity.Web - 令牌缓存序列化

下图显示 Microsoft.Identity.Web 的各种可能性及其对 Startup.cs 文件的影响:

此框图显示了 Startup.cs 中的服务配置选项,用于调用 Web API 和指定令牌缓存实现

注意

若要完全理解以下代码示例,请熟悉 ASP.NET Core 基础知识,尤其是依赖项注入选项

兑换授权代码的代码

Microsoft.Identity.Web 通过设置正确的 OpenID Connect 设置、订阅代码接收的事件和兑换代码来简化代码。 兑换授权代码不需要其他代码。 请参阅 Microsoft.Identity.Web 源代码,详细了解其原理。

保密客户端应用还可以使用客户端证书或客户端断言(而不是客户端密码)来证明其身份。 使用客户端断言是一种高级方案,客户端断言中对此进行了详细说明。

令牌缓存

重要

Web 应用或 Web API 的令牌缓存实现不同于桌面应用的实现,后者通常是基于文件的。 出于安全性和性能方面的原因,请务必确保对于 Web 应用和 Web API,每个用户帐户都有一个令牌缓存。 必须为每个帐户序列化令牌缓存。

ASP.NET Core 教程使用依赖关系注入,让你能够在应用的 Startup.cs 文件中确定令牌缓存实现。 Microsoft.Identity.Web 随附了令牌缓存序列化中所述的预生成令牌缓存序列化程序。 一个有意思的地方是,可以选择 ASP.NET Core 分布式内存缓存

// Use a distributed token cache by adding:
    services.AddMicrosoftIdentityWebAppAuthentication(Configuration, "AzureAd")
            .EnableTokenAcquisitionToCallDownstreamApi(
                initialScopes: new string[] { "https://microsoftgraph.chinacloudapi.cn/user.read" })
            .AddDistributedTokenCaches();

// Then, choose your implementation.
// For instance, the distributed in-memory cache (not cleared when you stop the app):
services.AddDistributedMemoryCache();

// Or a Redis cache:
services.AddStackExchangeRedisCache(options =>
{
 options.Configuration = "localhost";
 options.InstanceName = "SampleInstance";
});

// Or even a SQL Server token cache:
services.AddDistributedSqlServerCache(options =>
{
 options.ConnectionString = _config["DistCache_ConnectionString"];
 options.SchemaName = "dbo";
 options.TableName = "TestCache";
});

有关令牌缓存提供程序的详细信息,另请参阅 Microsoft.Identity.Web 的令牌缓存序列化一文,以及 Web 应用教程的 ASP.NET Core Web 应用教程 | 令牌缓存部分。

下一步

此时,当用户登录时,令牌存储在令牌缓存中。 让我们来看看随后是如何在 Web 应用的其他部分中使用它的。