从本地托管的 .NET 应用向 Azure 资源进行身份验证

在 Azure 外部(例如本地或第三方数据中心)托管的应用在访问 Azure 资源时应使用应用程序服务主体向 Azure 进行身份验证。 应用程序服务主体对象是使用 Azure 中的应用注册过程创建的。 创建应用程序服务主体时,将为应用生成客户端 ID 和客户端机密。 然后客户端 ID、客户端密码和租户 ID 将存储在环境变量中,以便在运行时 Azure 标识库可使用它们来对访问 Azure 的应用进行身份验证。

应为托管应用的每个环境创建不同的应用注册。 这样,就可以为每个服务主体配置特定于环境的资源权限,并确保部署到一个环境的应用不会与另一个环境中的 Azure 资源通信。

1 - 在 Azure 中注册应用程序

可以使用 Azure 门户或 Azure CLI 向 Azure 注册应用。

登录到 Azure 门户并执行以下步骤。

说明 屏幕快照
在 Azure 门户中:
  1. 在 Azure 门户顶部的搜索栏中输入“应用注册”。
  2. 在搜索栏下方显示的菜单中的“服务”标题下,选择标有“应用注册”的项。
显示如何使用 Azure 门户中的顶部搜索栏查找并导航到“应用注册”页的屏幕截图。
在“应用注册”页上,选择“+ 新建注册”。 显示“新建注册”按钮在“应用注册”页中的位置的屏幕截图。
在“注册应用程序”页上,按如下所示填写窗体:
  1. 名称 → 输入该应用注册在 Azure 中的名称。 建议在此名称中包含应用名称和应用注册所针对的环境(测试、生产)。
  2. 支持的帐户类型 → 仅限此组织目录中的帐户。
选择“注册”以注册应用并创建应用程序服务主体。
显示如何填写“注册应用程序”页的屏幕截图:为应用命名,并将“支持的帐户类型”指定为“仅限此组织目录中的帐户”。
在应用的“应用注册”页上:
  1. 应用程序(客户端) ID → 这是在本地开发期间由应用用来访问 Azure 的应用 ID。 将此值复制到文本编辑器中的临时位置,因为在稍后的步骤中需要用到。
  2. 目录(租户) ID → 应用在向 Azure 进行身份验证时也需要此值。 将此值复制到文本编辑器中的临时位置,因为在稍后的步骤中也需要用到。
  3. 客户端凭据 → 必须先为应用设置客户端凭据,然后应用才能向 Azure 进行身份验证并使用 Azure 服务。 选择“添加证书或机密”以添加应用的凭据。
完成应用注册后的“应用注册”页的屏幕截图。此屏幕截图显示了应用程序 ID 和租户 ID 的位置,在稍后的步骤中需要用到。它还显示了用来为应用添加应用程序机密的链接的位置。
在“证书和机密”页上,选择“+ 新建客户端密码”。 显示“证书和机密”页上用于创建新客户端机密的链接的位置的屏幕截图。
页面右侧会弹出“添加客户端机密”对话框。 在此对话框中:
  1. 说明 → 输入值“当前”。
  2. 过期时间 → 选择值“24 个月”。
选择“添加”以添加机密。

重要说明:在日历中设置机密过期日期之前发出的提醒。这样,可以在此机密过期之前添加新机密和更新应用,并避免应用中出现服务中断。
显示为应用注册过程创建的应用程序服务主体添加了新客户端机密的页面的屏幕截图。
在“证书和机密”页上,你将看到客户端密码的值。

将此值复制到文本编辑器中的临时位置,因为在稍后的步骤中需要用到。

重要提示:此值只显示这一次。退出或刷新此页面后,将无法再次看到此值。 可以在不使此客户端密码失效的情况下添加一个额外的客户端密码,但没法再次看到此值。
显示包含生成的客户端机密的页面的屏幕截图。

2 - 将角色分配到应用程序服务主体

接下来,需要确定应用在哪些资源上需要哪些角色(权限),并将这些角色分配到应用。 可以在资源、资源组或订阅范围分配角色。 此示例演示如何在资源组范围为服务主体分配角色,因为大多数应用将其所有 Azure 资源分组到单个资源组中。

说明 屏幕快照
使用 Azure 门户顶部的搜索框搜索资源组名称,找到应用的资源组。

在对话框中的“资源组”标题下选择资源组名称,导航到该资源组。
显示如何使用 Azure 门户中的顶部搜索框来查找并导航到要为其分配角色(权限)的资源组的屏幕截图。
在资源组的页面上,从左侧菜单中选择“访问控制(IAM)”。 资源组页面的屏幕截图,其中显示了“访问控制(IAM)”菜单项的位置。
在“访问控制(IAM)”页上
  1. 选择“角色分配”选项卡。
  2. 从顶部菜单中选择“+ 添加”,然后从出现的下拉菜单中选择“添加角色分配”。
显示如何导航到角色分配选项卡,以及用于将角色分配添加到资源组的按钮的位置的屏幕截图。
“添加角色分配”页列出了可为资源组分配的所有角色。
  1. 使用搜索框筛选列表,以便以更容易操作的大小显示内容。 此示例演示如何筛选存储 Blob 角色。
  2. 选择要分配的角色。
选择“下一步”转到下一屏幕。
显示如何筛选和选择要添加到资源组的角色分配的屏幕截图。
在下一个“添加角色分配”页面中,可以指定要将角色分配给哪个用户。
  1. 在“将访问权限分配给”下选择“用户、组或服务主体”。
  2. 在“成员”下选择“+ 选择成员”。
Azure 门户的右侧将打开一个对话框。
该屏幕截图显示了用于选择将角色分配到 Microsoft Entra 组的单选按钮,以及用于选择要将角色分配到的组的链接。
在“选择成员”对话框中
  1. “选择”文本框可用于筛选订阅中的用户和组列表。 如果需要,请键入为应用创建的服务主体的前几个字符以筛选列表。
  2. 选择与应用程序关联的服务主体。
在对话框底部选择“选择”以继续。
显示如何在“选择成员”对话框中为应用程序筛选和选择 Microsoft Entra 组的屏幕截图。
现在,该服务主体将在“添加角色分配”屏幕上显示为选中状态。

选择“查看 + 分配”转到最后一页,然后再次选择“查看 + 分配”完成该过程。
显示已完成的“添加角色分配”页,以及用于完成该过程的“查看 + 分配”按钮的位置的屏幕截图。

3 - 为应用程序配置环境变量

在运行时,DefaultAzureCredential 对象将在一组环境变量中查找服务主体凭据。 使用 .NET 时,可以根据你的工具和环境以多种方式配置环境变量。

无论选择哪种方法,都请在处理服务主体时配置以下环境变量:

  • AZURE_CLIENT_ID → 应用 ID 值。
  • AZURE_TENANT_ID → 租户 ID 值。
  • AZURE_CLIENT_SECRET → 为应用生成的密码/凭据。

如果应用托管在 IIS 中,建议为每个应用池设置环境变量,以隔离应用之间的设置。

appcmd.exe set config -section:system.applicationHost/applicationPools /+"[name='Contoso'].environmentVariables.[name='ASPNETCORE_ENVIRONMENT',value='Production']" /commit:apphost
appcmd.exe set config -section:system.applicationHost/applicationPools /+"[name='Contoso'].environmentVariables.[name='AZURE_CLIENT_ID',value='00000000-0000-0000-0000-000000000000']" /commit:apphost
appcmd.exe set config -section:system.applicationHost/applicationPools /+"[name='Contoso'].environmentVariables.[name='AZURE_TENANT_ID',value='11111111-1111-1111-1111-111111111111']" /commit:apphost
appcmd.exe set config -section:system.applicationHost/applicationPools /+"[name='Contoso'].environmentVariables.[name='AZURE_CLIENT_SECRET',value='=abcdefghijklmnopqrstuvwxyz']" /commit:apphost

还可以在 applicationHost.config 文件中使用 applicationPools 元素直接配置这些设置:

<applicationPools>
   <add name="CorePool" managedRuntimeVersion="v4.0" managedPipelineMode="Classic">
      <environmentVariables>
         <add name="ASPNETCORE_ENVIRONMENT" value="Development" />
         <add name="AZURE_CLIENT_ID" value="00000000-0000-0000-0000-000000000000" />
         <add name="AZURE_TENANT_ID" value="11111111-1111-1111-1111-111111111111" />
         <add name="AZURE_CLIENT_SECRET" value="=abcdefghijklmnopqrstuvwxyz" />
      </environmentVariables>
   </add>
</applicationPools>

4 - 在应用程序中实现 DefaultAzureCredential

DefaultAzureCredential 是一个带个人观点的有序机制序列,用于向 Microsoft Entra 进行身份验证。 每个身份验证机制都是一个派生自 TokenCredential 类的类,称为“凭据”。 在运行时,DefaultAzureCredential 尝试使用第一个凭据进行身份验证。 如果该凭据无法获取访问令牌,则会尝试序列中的下一个凭据,以此类推,直到成功获取访问令牌。 这样,应用就可在不同的环境中使用不同的凭据,而无需编写特定于环境的代码。

DefaultAzureCredential 按哪种顺序和在哪个位置查找凭据见于 DefaultAzureCredential

若要使用 DefaultAzureCredential,请将 Azure.IdentityMicrosoft.Extensions.Azure 包添加到应用程序:

在所选终端中,导航到应用程序项目目录并运行以下命令:

dotnet add package Azure.Identity
dotnet add package Microsoft.Extensions.Azure

使用各种 Azure SDK 客户端库中的专用客户端类访问 Azure 服务。 应注册这些类和你自己的自定义服务,以便可以在整个应用中通过依赖项注入来访问它们。 在 Program.cs 中,完成以下步骤以注册客户端类和 DefaultAzureCredential

  1. 通过 using 指令包含 Azure.IdentityMicrosoft.Extensions.Azure 命名空间。
  2. 使用相应的 Add 前缀扩展方法注册 Azure 服务客户端。
  3. DefaultAzureCredential 的实例传递给 UseCredential 方法。

例如:

using Microsoft.Extensions.Azure;
using Azure.Identity;

builder.Services.AddAzureClients(clientBuilder =>
{
    clientBuilder.AddBlobServiceClient(
        new Uri("https://<account-name>.blob.core.windows.net"));
    clientBuilder.UseCredential(new DefaultAzureCredential());
});

UseCredential 的替代方法是直接实例化 DefaultAzureCredential

using Azure.Identity;

builder.Services.AddSingleton<BlobServiceClient>(_ =>
    new BlobServiceClient(
        new Uri("https://<account-name>.blob.core.windows.net"),
        new DefaultAzureCredential()));

上述代码在本地开发工作站上运行时,它会在应用程序服务主体的环境变量或本地安装的开发人员工具(如 Visual Studio)中查找一组开发人员凭据。 在本地开发期间,两种方法都可用于对访问 Azure 资源的应用进行身份验证。

部署到 Azure 时,此代码也可以对访问 Azure 资源的应用进行身份验证。 DefaultAzureCredential 可以检索环境设置和托管标识配置,以自动向其他服务进行身份验证。