使用 Microsoft.Identity.Web 配置无证书身份验证

本文介绍如何配置无证书身份验证,以便应用程序无需管理证书或客户端机密即可使用Microsoft Entra ID进行身份验证。 应用使用由Azure托管标识支持的联合标识凭据(FIC)来获取令牌,从而消除凭据轮换、减少机密蔓延并简化Azure部署。

Microsoft。Identity.Web 通过版本 2.12.0 及更高版本中提供的 SignedAssertionFromManagedIdentity 凭据源类型支持无证书身份验证。


了解无证书身份验证

本部分介绍无证书身份验证的工作原理以及何时使用它。

传统上,机密客户端应用程序通过提供客户端机密或证书来证明其身份Microsoft Entra ID。 这两种方法都需要管理凭据生命周期-在机密过期之前轮换机密、续订证书并安全地存储它们。

联合标识凭据(FIC)改变此模型。 使用 FIC,可以在应用注册和托管标识之间配置信任关系。 应用程序需要进行身份验证时:

  1. Microsoft.Identity.Web 从 Azure 主机上的托管标识终结点请求令牌。
  2. 该库使用托管标识令牌作为签名断言,通过Microsoft Entra ID进行身份验证。
  3. Microsoft Entra ID 验证已签名的声明,以匹配应用注册中的联合凭据配置。
  4. Microsoft Entra ID为请求的资源颁发访问令牌。

结果是完全无凭据的部署,其中配置、代码或环境变量中不存在机密或证书。

选择正确的身份验证方法

下表可帮助你确定无证书身份验证是否是正确的选择。

情景 建议的方法
应用在Azure上运行,需要零凭据管理 使用 FIC 的无证书
应用在 Azure 上运行,但需要支持本地部署的回退机制 以 FIC 为主要凭据的证书认证
应用在Azure外部运行(本地、其他云) 证书或客户端机密
在本地计算机上开发和测试 本地存储库中的客户端机密或证书

先决条件

在开始之前,请验证你是否具有以下资源和工具:

  • Azure 订阅。 如果没有帐户,请创建一个免费帐户
  • 在 Microsoft Entra ID 中进行的 应用注册,具备您方案所需的 API 权限。
  • Azure中的 托管标识,可以是系统分配在计算资源上的托管标识,也可以是独立的用户分配托管标识。
  • Microsoft.Identity.Web 2.12.0 或更高版本安装在项目中。
  • 支持托管标识的Azure计算资源,例如Azure 应用服务、Azure Kubernetes 服务 (AKS)、Azure 容器应用或Azure 虚拟机。

步骤 1:创建或标识托管标识

可以使用系统分配的托管标识或用户分配的托管标识。 如果尚未创建一个,请按照方案的说明进行操作。

选项 A:使用系统分配的托管标识

系统分配的托管标识与Azure资源的生命周期相关联。 在应用服务等资源上启用系统分配的标识时,Azure会自动创建标识。

  1. Azure 门户中,导航到计算资源(例如应用服务)。
  2. 从左侧导航菜单中选择“标识”
  3. 在“系统分配”选项卡上,将“状态”设置为“启用”
  4. 选择“ 保存 ”并确认操作。
  5. 创建标识后,复制 对象(主体)ID。 配置联合凭据时需要此值。

选项 B:创建用户分配的托管标识

用户分配的托管身份是可以分配给一个或多个计算资源的独立 Azure 资源。

  1. Azure 门户中,搜索 Managed Identities并选择它。
  2. 选择“创建”
  3. 选择 订阅资源组区域,并为标识输入名称
  4. 选择 “查看 + 创建”,然后选择 “创建”。
  5. 部署完成后,打开新的托管身份资源。
  6. “概述”页复制客户端 ID。 应用程序配置需要此值。

步骤 2:在 Azure 门户中配置联合标识凭据

联合标识凭据在应用注册和托管标识之间建立信任关系。 按照以下步骤创建一个:

  1. Azure 门户中,转到 Microsoft Entra ID>应用注册

  2. 选择您的应用程序所使用的应用注册信息。

  3. 在左侧导航菜单中,选择 “证书和机密”。

  4. 选择“ 联合凭据 ”选项卡。

  5. 选择添加凭据

  6. “联合凭据方案”下,选择“客户托管密钥”或其他颁发者(可用选项取决于门户版本)。

  7. 配置以下字段:

    领域 价值
    颁发者 https://login.microsoftonline.com/{tenant-id}/v2.0 — 将 {tenant-id}替换为Microsoft Entra租户 ID。
    主体标识符 托管标识 的对象(主体)ID 。 对于系统分配,可在资源的“标识页面”上找到此项。 对于用户分配,可在“ 主体 ID”下的“托管标识概述”页上找到此信息。
    名称 描述性名称,例如 fic-managed-identity-prod
    观众 api://AzureADTokenExchange (默认值)。
  8. 选择 并添加

重要

使用者标识符必须与托管标识的对象(主体)ID 完全匹配。 不匹配会导致身份验证失败并出现 AADSTS70021 错误。

使用 Azure CLI 配置联合标识凭据

或者,使用Azure CLI创建联合凭据。 以下命令在你的应用注册上创建凭据:

az ad app federated-credential create \
    --id <app-object-id> \
    --parameters '{
        "name": "fic-managed-identity-prod",
        "issuer": "https://login.microsoftonline.com/<tenant-id>/v2.0",
        "subject": "<managed-identity-principal-id>",
        "audiences": ["api://AzureADTokenExchange"],
        "description": "FIC for production managed identity"
    }'

按 Azure 服务分类的颁发者 URL

联合凭据中的颁发者 URL 取决于托管应用程序的Azure服务:

Azure服务 颁发者 URL
Azure 应用服务/Azure Functions https://login.microsoftonline.com/{tenant-id}/v2.0
Azure 容器应用 https://login.microsoftonline.com/{tenant-id}/v2.0
Azure Kubernetes Service (AKS) 您集群的 OIDC 发行者 URL(可以使用 az aks show --query oidcIssuerProfile.issuerUrl 检索)
Azure 虚拟机 https://login.microsoftonline.com/{tenant-id}/v2.0

主题标识符格式

使用者标识符的格式取决于托管标识类型:

系统分配的托管标识 - 使用资源 标识 页中的对象(主体)ID。 这是 GUID 值,例如 a1b2c3d4-e5f6-7890-abcd-ef1234567890

用户分配的托管标识 - 使用托管标识资源的“概述”页中的主体 ID(也称为对象 ID)。 这也是 GUID 值。

注释

对于具有工作负荷标识的 AKS,使用者标识符使用不同的格式: system:serviceaccount:{namespace}:{service-account-name}. 此值必须与 Pod 使用的 Kubernetes 服务帐户匹配。


步骤 3:配置应用程序

更新 appsettings.json

ClientCredentials 分区添加到 AzureAd 配置。 将 SourceType 设置为 SignedAssertionFromManagedIdentity

对于用户分配的托管标识

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "TenantId": "YOUR_TENANT_ID",
    "ClientId": "YOUR_CLIENT_ID",
    "ClientCredentials": [
      {
        "SourceType": "SignedAssertionFromManagedIdentity",
        "ManagedIdentityClientId": "USER_ASSIGNED_MSI_CLIENT_ID"
      }
    ]
  }
}

替换以下占位符:

占位符 说明
YOUR_TENANT_ID Microsoft Entra租户标识。
YOUR_CLIENT_ID 应用注册的应用程序(客户端)ID。
USER_ASSIGNED_MSI_CLIENT_ID 用户分配的托管标识的客户端 ID(来自标识的“概述”页)。

对于系统分配的托管标识

使用系统分配的托管标识时,省略该 ManagedIdentityClientId 属性。 Microsoft。Identity.Web 自动使用主机的系统分配标识:

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "TenantId": "YOUR_TENANT_ID",
    "ClientId": "YOUR_CLIENT_ID",
    "ClientCredentials": [
      {
        "SourceType": "SignedAssertionFromManagedIdentity"
      }
    ]
  }
}

在 Program.cs 中注册服务

启动配置中不需要任何特殊的代码更改。 标准 Microsoft.Identity.Web 注册方法会自动读取 ClientCredentials 部分。

以下示例为登录用户并调用下游 API 的 Web 应用注册身份验证:

// For a web app that signs in users and calls downstream APIs
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
    .EnableTokenAcquisitionToCallDownstreamApi()
    .AddInMemoryTokenCaches();

以下示例为调用下游 API 的 Web API 注册身份验证:

// For a web API that calls downstream APIs
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"))
    .EnableTokenAcquisitionToCallDownstreamApi()
    .AddInMemoryTokenCaches();

以下示例为没有用户交互的守护程序应用程序注册身份验证:

// For a daemon application (no user interaction)
builder.Services.AddAuthentication()
    .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));

builder.Services.AddTokenAcquisition()
    .AddInMemoryTokenCaches();

Microsoft。Identity.Web 检测 SignedAssertionFromManagedIdentity 源类型,并透明地处理令牌交换。


比较系统分配的托管标识和用户分配的托管标识

选择最适合体系结构的托管标识类型。 以下部分概述了权衡。

系统分配的托管标识

系统分配的标识是使用其所属Azure资源自动创建和删除的。

优点:

  • 没有单独的要管理的资源 - 标识生命周期与计算资源匹配。
  • 单资源部署的设置更简单。
  • 配置中不需要 ManagedIdentityClientId

注意事项:

  • 不能跨多个资源共享标识。
  • 如果删除并重新创建资源,则标识会更改 — 必须更新联合标识凭据。

最适合: 一个计算资源映射到一个应用注册的单实例部署。

用户分配的托管标识

用户分配的标识是具有其生命周期的独立Azure资源。

优点:

  • 跨多个计算资源共享单个标识(例如,不同区域中的多个应用服务实例)。
  • 身份标识独立于计算资源生命周期而持续存在。
  • 在部署计算资源之前预先创建和预配置。

注意事项:

  • 要管理的其他Azure资源。
  • 您必须在配置中指定 ManagedIdentityClientId

最适合: 多实例或多区域部署、蓝绿部署模式,以及经常重新创建计算资源的方案。


部署到 Azure 云计算服务

配置应用程序后,将其部署到支持托管标识的Azure计算服务。

Azure App 服务

  1. 在应用服务上启用托管标识(请参阅 步骤 1)。

  2. 使用首选方法(Visual Studio、Azure CLI、GitHub Actions)将应用程序部署到应用服务。

  3. 确保你已部署配置中的AzureAd部分与步骤 3中的设置匹配。

  4. 如果使用用户分配的托管标识,请将其分配给应用服务:

    az webapp identity assign \
      --resource-group <resource-group> \
      --name <app-service-name> \
      --identities <managed-identity-resource-id>
    
  5. 重启应用服务以启用身份分配。

Azure Kubernetes 服务 (AKS)

对于 AKS,请使用工作负荷标识将 Kubernetes 服务帐户与托管标识相关联。 完成以下步骤:

  1. 在 AKS 群集上启用工作负荷标识功能:

    az aks update \
      --resource-group <resource-group> \
      --name <aks-cluster-name> \
      --enable-oidc-issuer \
      --enable-workload-identity
    
  2. 创建一个带有托管标识客户端 ID 注释的 Kubernetes 服务帐户:

    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: my-app-sa
      namespace: default
      annotations:
        azure.workload.identity/client-id: "<USER_ASSIGNED_MSI_CLIENT_ID>"
    
  3. 创建将 AKS OIDC 颁发者链接到托管标识的联合凭据。

  4. 将您的 Pod 进行配置以使用服务帐户:

    apiVersion: v1
    kind: Pod
    metadata:
      name: my-app
      namespace: default
      labels:
        azure.workload.identity/use: "true"
    spec:
      serviceAccountName: my-app-sa
      containers:
        - name: my-app
          image: <your-container-image>
    
  5. 部署 Pod。 工作负载身份 Webhook 为托管标识令牌终结点注入所需的环境变量。

Azure 容器应用

  1. 使用托管身份创建或更新容器应用程序:

    az containerapp identity assign \
      --resource-group <resource-group> \
      --name <container-app-name> \
      --user-assigned <managed-identity-resource-id>
    
  2. 使用适当的 AzureAd 配置部署容器映像。

  3. 托管身份标识令牌终结点在容器内部自动可用。


从证书迁移到无证书身份验证

如果应用程序当前使用基于证书的身份验证,则可以使用最少的配置更改迁移到无证书身份验证。

完成迁移步骤

  1. 为Azure计算资源创建托管标识(请参阅 Step 1)。

  2. 将联合标识凭据添加到 应用注册(请参阅 步骤 2)。

  3. 更新配置 以添加 SignedAssertionFromManagedIdentity 凭据。 在迁移期间,您可以将现有证书凭据保留作为备用方案:

    {
      "AzureAd": {
        "Instance": "https://login.microsoftonline.com/",
        "TenantId": "YOUR_TENANT_ID",
        "ClientId": "YOUR_CLIENT_ID",
        "ClientCredentials": [
          {
            "SourceType": "SignedAssertionFromManagedIdentity",
            "ManagedIdentityClientId": "USER_ASSIGNED_MSI_CLIENT_ID"
          },
          {
            "SourceType": "KeyVault",
            "KeyVaultUrl": "https://your-keyvault.vault.azure.net",
            "KeyVaultCertificateName": "your-cert-name"
          }
        ]
      }
    }
    

    Microsoft.Identity.Web 按顺序尝试凭据源。 在 Azure 上运行时,第一个凭据(SignedAssertionFromManagedIdentity)会成功。 如果失败(例如,在本地开发期间),库会回退到证书。

  4. 在应用于生产环境之前,在过渡环境中部署和验证

  5. 确认无证书身份验证在生产环境中正常工作后,请从配置中删除证书凭据

  6. 从Azure 密钥保管库和应用注册中删除证书,当它们不再需要时。

在配置前后进行比较

以下示例演示了从基于证书的身份验证更改为无证书身份验证的配置。

之前(基于证书):

{
  "AzureAd": {
    "ClientCredentials": [
      {
        "SourceType": "KeyVault",
        "KeyVaultUrl": "https://your-keyvault.vault.azure.net",
        "KeyVaultCertificateName": "your-cert-name"
      }
    ]
  }
}

之后(无证书):

{
  "AzureAd": {
    "ClientCredentials": [
      {
        "SourceType": "SignedAssertionFromManagedIdentity",
        "ManagedIdentityClientId": "USER_ASSIGNED_MSI_CLIENT_ID"
      }
    ]
  }
}

解决常见问题

使用以下指南诊断和解决无证书身份验证的问题。

AADSTS70021:找不到匹配的联合标识记录

原因: 联合标识凭据中的使用者标识符与托管标识的对象(主体)ID 不匹配。

解决方法:

  1. 在 Azure 门户中,导航到托管标识资源,并从 Overview 页复制 Principal ID(也称为对象 ID)。
  2. 导航到应用注册 >证书和机密>联合凭据
  3. 验证 “使用者标识符 ”字段是否与主体 ID 完全匹配。
  4. 如果值不匹配,请删除凭据,然后使用正确的使用者标识符重新创建凭据。

AADSTS700024:客户端声明不在其有效时间范围内

原因: 用作已签名断言的托管标识令牌已过期或系统时钟偏斜。

解决方法:

  • 验证Azure资源上的系统时钟是否准确。
  • 请重启应用程序以强制请求新的托管标识令牌。
  • 如果在容器中运行,请确保容器的时钟与主机同步。

ManagedIdentityException:托管标识终结点不可用

Cause:应用程序无法访问 Azure 实例元数据服务(IMDS)或托管标识令牌终结点。

解决方法:

  • 确认应用程序在支持托管标识的Azure计算资源上运行。
  • 验证是否已启用“托管身份”并将其分配给计算资源。
  • 对于 AKS,请验证工作负荷标识 Webhook 是否正在运行,并且 Pod 具有正确的服务帐户注释。
  • 对于本地开发,预计会出现此错误。 使用备用凭据源(请参阅 迁移步骤)。

AADSTS700016:目录中找不到应用程序

原因: 配置 ClientId 中的配置与指定租户中的有效应用注册不匹配。

解决方法:

  • 验证ClientId是否与您的应用注册的应用程序(客户端)ID匹配。
  • 验证 TenantId 是否匹配注册应用的租户。

启用调试日志记录

原因: 凭据源顺序或配置不匹配可能会导致库跳过 FIC 凭据。

解决方法:

  1. 启用 Microsoft.Identity.Web 中的日志记录以查看详细的令牌获取步骤。 以下代码为标识库配置调试级日志记录:

    builder.Services.AddLogging(logging =>
    {
        logging.AddConsole();
        logging.SetMinimumLevel(LogLevel.Debug);
        logging.AddFilter("Microsoft.Identity", LogLevel.Debug);
    });
    
  2. 查看日志,了解有关库尝试使用的凭据来源及返回的任何错误消息。

未检测到用户分配的托管标识

原因: 将多个用户分配的托管标识分配给计算资源时,如果 ManagedIdentityClientId 未指定,库可能会使用错误的托管标识。

解决方法:

  • 使用用户分配的托管标识时,请始终指定 ManagedIdentityClientId 属性。
  • 验证客户端 ID 是否与你为其配置了联合标识凭据的标识匹配。

审查安全优势

使用 FIC 进行无证书身份验证比传统的基于凭据的方法具有重要的安全优势:

没有泄露机密

由于配置或部署项目中不存在证书文件、PFX 密码或客户端密码,因此攻击者无需提取任何密码。 即使攻击者获得对配置文件的读取访问权限,他们也不能从Azure外部模拟应用程序。

无凭据轮换

托管身份令牌是短期有效的,并由 Azure 平台自动刷新。 无需实现轮换计划、监视过期日期或跨部署协调凭据更新。

减少攻击面

只能从分配给标识的特定 Azure 资源访问托管标识令牌终结点。 攻击者无法使用来自其他主机、网络或云环境的凭据。

合规性简化

如果没有长期凭据,可以消除多种符合性问题:

  • 源代码管理、环境变量或配置文件中没有存储机密。
  • 没有用于审核、轮换或撤销的关键材料。
  • 没有要维护的证书基础结构(CA,续订过程)。

深层防御

将无证书身份验证与其他Azure安全功能相结合,以便进行分层保护:

  • Azure RBAC:控制哪些标识可以访问哪些资源。
  • 条件访问:根据标识风险、位置和设备状态应用策略。
  • 专用终结点:限制对 Azure 资源的网络访问。
  • Microsoft Defender for Cloud:监视可疑身份验证模式。