如何在 Azure 上对 Python 应用程序进行身份验证和授权

大多数部署到 Azure 的云应用程序都需要访问其他 Azure 资源,如存储、数据库、存储的机密等等。 若要访问这些资源,必须对应用程序进行身份验证和授权:

  • 身份验证通过 Azure Active Directory 验证应用的标识。

  • 授权确定经过身份验证的应用可以对任何给定资源执行哪些操作。 授权操作由分配给该资源的应用标识的角色定义。 在少数情况下(如 Azure Key Vault),授权还由分配给应用标识的其他访问策略确定。

本文介绍了身份验证和授权的详细信息:

  • 如何分配应用标识。
  • 如何向标识授予权限。
  • 身份验证和授权的发生方式和时间。

次要文章介绍了具体的身份验证方法:

如何分配应用标识

在 Azure 上,应用标识由服务主体定义。 (服务主体是特定类型的“安全主体”,用于标识应用或服务,即一段代码,而不是一个用户或一组用户。)

涉及的服务主体取决于应用的运行位置,如以下各节中所述。

在 Azure 上运行应用时的标识

在云中(例如在生产环境中)运行时,应用最常使用系统分配的托管标识(以前称为 MSI)。 使用托管标识,可以在为资源分配角色和权限时使用应用的名称。 Azure 自动管理基础服务主体,并使用其他 Azure 资源自动对应用进行身份验证。 因此,你无需直接处理服务主体。 此外,应用代码永远无需处理 Azure 资源的访问令牌、机密或连接字符串,从而降低了此类信息可能被泄漏或以其他方式遭到入侵的风险。

托管标识的配置取决于用于托管应用的服务。 请参阅支持托管标识的服务一文,以获取指向每个服务的说明的链接。 例如,对于部署到 Azure 应用服务的 Web 应用,可通过 Azure 门户中的“标识” > “系统分配”选项或使用 Azure CLI 中的 az webapp identity assign 命令启用托管标识 。

如果无法使用托管标识,请改为手动将应用程序注册到 Azure Active Directory。 注册操作可为应用分配服务主体,供你在分配角色和权限时使用。 有关详细信息,请参阅注册应用程序

在本地运行应用时的标识

在开发过程中,你通常希望在开发人员工作站上运行和调试应用代码,同时让此代码仍可访问云中的 Azure 资源。 在这种情况下,可以通过 Azure Active Directory 创建一个单独的服务主体,专门用于本地开发。 再次将角色和权限分配给该服务主体,以获得相关资源。 通常,你授权此开发标识仅访问非生产资源。

要详细了解如何创建本地服务主体并使其可供 Azure 库使用,请参阅配置本地开发环境。 完成这个一次性配置后,你可在本地和云中运行相同的应用代码,而无需进行任何特定于环境的修改。

每个开发人员都应具有自己的服务主体,这些服务主体可以安全地存储在他们的工作站上的用户帐户中,且永远不要将其存储在源代码管理存储库中。 如有任一个服务主体被窃取或泄露,你可轻松删除它以撤销其所有权限,然后为该开发人员重新创建服务主体。 有关详细信息,请参阅如何管理服务主体

备注

尽管可以使用自己的 Azure 用户凭据运行应用,但这样做并不能帮助你创建将应用部署到云时所需的特定资源权限。 最好是设置用于开发的服务主体,并为其分配所需的角色和权限,然后你可以使用已部署应用的托管标识或服务主体复制这些角色和权限。

为标识分配角色和权限

了解应用在 Azure 上和在本地运行时的标识后,就可以使用基于角色的访问控制 (RBAC) 通过 Azure 门户或 Azure CLI 授予权限。 如需了解完整的详细信息,请参阅如何向应用标识或服务主体分配角色权限

身份验证和授权何时发生?

使用用于 Python 的 Azure 库 (SDK) 编写应用代码时,请使用以下模式访问 Azure 资源:

  1. 使用 Azure 标识库中的类获取凭据。 凭据描述应用标识,并包含或可以获取对请求进行身份验证所需的数据。

  2. 使用该凭据获取目标资源的客户端对象。 (每种类型的资源在 Azure 库中都有其自己的客户端对象,你可以向其提供资源的 URL。)

  3. 尝试通过客户端对象访问或修改资源,该对象将生成对资源的 REST API 的 HTTP 请求。 API 调用是 Azure 随后对应用标识进行身份验证和检查授权的点。

以下代码通过尝试访问 Azure Key Vault 来描述和演示这些步骤。

import os
from azure.identity import DefaultAzureCredential
from azure.keyvault.secrets import SecretClient

# Acquire the resource URL. In this code we assume the resource URL is in an
# environment variable, KEY_VAULT_URL in this case.

vault_url = os.environ["KEY_VAULT_URL"]


# Acquire a credential object for the app identity. When running in the cloud,
# DefaultAzureCredential uses the app's managed identity (MSI) or user-assigned service principal.
# When run locally, DefaultAzureCredential relies on environment variables named
# AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, and AZURE_TENANT_ID.

credential = DefaultAzureCredential()


# Acquire an appropriate client object for the resource identified by the URL. The
# client object only stores the given credential at this point but does not attempt
# to authenticate it.
#
# **NOTE**: SecretClient here is only an example; the same process applies to all
# other Azure client libraries.

secret_client = SecretClient(vault_url=vault_url, credential=credential)

# Attempt to perform an operation on the resource using the client object (in
# this case, retrieve a secret from Key Vault). The operation fails for any of
# the following reasons:
#
# 1. The information in the credential object is invalid (for example, the AZURE_CLIENT_ID
#    environment variable cannot be found).
# 2. The app identity cannot be authenticated using the information in the credential object.
# 3. The app identity is not authorized to perform the requested operation on the
#    resource (identified in this case by the vault_url.

retrieved_secret = secret_client.get_secret("secret-name-01")

同样,在代码通过客户端对象向 Azure REST API 发出特定请求之前,不会进行身份验证或授权。 用于创建 DefaultAzureCredential 的语句(请参阅下一节)仅在内存中创建一个客户端对象,但不执行其他检查。

创建 SDK SecretClient 对象也不涉及与相关资源的通信。 SecretClient 对象只是基础 Azure REST API 的包装器,仅存在于应用的运行时内存中。

仅当代码调用 get_secret 方法时,客户端对象才会生成对 Azure 的相应 REST API 调用。 然后,get_secret 的 Azure 终结点会对调用方的标识进行身份验证并检查授权。

另请参阅