创建云应用程序时,开发人员需要在本地工作站上调试和测试应用程序。 在本地开发期间,应用程序在开发人员工作站上运行时,它仍必须向应用使用的任何 Azure 服务进行身份验证。 本文介绍如何设置在本地开发期间要使用的专用应用程序服务主体对象。

使用专用应用程序服务主体进行本地开发,可以在应用开发期间遵循最低特权原则。 由于权限的范围限定为开发期间应用所需的内容,因此阻止应用代码意外访问供其他应用使用的 Azure 资源。 这也可以防止应用移动到生产环境时出现 bug,因为应用在开发环境中过度特权。

在 Azure 中注册应用时,将为应用设置应用程序服务主体。 注册应用进行本地开发时,建议执行以下操作:

  • 为处理应用的每个开发人员创建单独的应用注册。 这将为每个开发人员创建单独的应用程序服务主体,以便在本地开发期间使用,并避免开发人员需要为单个应用程序服务主体共享凭据。
  • 为每个应用创建单独的应用注册。 这将应用的权限限定为仅应用所需的权限。
  • 在本地开发期间,环境变量使用应用程序服务主体的标识进行设置。 用于 Python 的 Azure SDK 读取这些环境变量,并使用此信息向所需的 Azure 资源对应用进行身份验证。

    1 - 在 Azure 中注册应用程序

    应用程序服务主体对象是在 Azure 中使用应用注册创建的。 可以使用 Azure 门户 或 Azure CLI 完成此操作。

    Azure 门户 Azure CLI “注册应用程序 ”页上,填写表单,如下所示。
  • 名称 →输入 Azure 中的应用注册的名称。 建议使用此名称包括应用名称、应用注册的用户,以及“dev”等标识符,以指示此应用注册用于本地开发。
  • 此组织目录中 支持的帐户类型 →帐户。
  • 选择 “注册 ”以注册应用并创建应用程序服务主体。 在应用的“应用注册”页上:
    1. 应用程序 (客户端) ID →这是应用在本地开发期间用于访问 Azure 的应用 ID。 将此值复制到文本编辑器中的临时位置,因为将来的步骤中需要它。
    2. 目录 (租户) ID →应用在向 Azure 进行身份验证时也需要此值。 将此值复制到文本编辑器中的临时位置,在以后的步骤中也需要它。
    3. 客户端凭据 →必须先为应用设置客户端凭据,然后应用才能向 Azure 进行身份验证并使用 Azure 服务。 选择 “添加证书或机密 ”以添加应用的凭据。
    4. 可以在 Azure Cloud Shell 中或 装有 Azure CLI 的工作站上运行 Azure CLI 命令。

      首先,使用 az ad sp create-for-rbac 命令为应用创建新的服务主体。 这还会同时为应用创建应用注册。

      az ad sp create-for-rbac --name {service-principal-name}
      

      此命令的输出如下所示。 建议将此输出复制到文本编辑器中的临时文件中,因为将来的步骤需要这些值,因为这是服务主体 (密码) 客户端密码的唯一位置。 但是,以后可以添加新密码,而无需使服务主体或现有密码失效(如果需要)。

      "appId": "00000000-0000-0000-0000-000000000000", "displayName": "{service-principal-name}", "password": "abcdefghijklmnopqrstuvwxyz", "tenant": "11111111-1111-1111-1111-111111111111"

      2 - 创建用于本地开发的 Azure AD 安全组

      由于通常有多个开发人员处理应用程序,因此建议创建 Azure AD 组来封装 (权限,) 应用在本地开发中所需的角色,而不是将角色分配给单个服务主体对象。 这具有以下优势。

    5. 每个开发人员都保证分配相同的角色,因为角色是在组级别分配的。
    6. 如果应用需要新角色,则只需将其添加到应用的 Azure AD 组。
    7. 如果新开发人员加入团队,则会为开发人员创建新的应用程序服务主体并将其添加到组中,确保开发人员有权处理应用。
    8. az ad group create 命令用于在 Azure Active Directory 中创建组。 --display-name--main-nickname 参数是必需的。 分配给组的名称应基于应用程序的名称。 在组名称中包含一个短语(如“local-dev”)也很有用,以指示组的用途。

      az ad group create \
          --display-name MyDisplay \
          --mail-nickname MyDisplay  \
          --description \<group-description>
      

      若要将成员添加到组,需要应用程序服务主体的对象 ID,应用程序 ID 与应用程序 ID 不同。使用 az ad sp list 列出可用的服务主体。 参数 --filter 命令接受 OData 样式筛选器,可用于按如下所示筛选列表。 参数 --query 将列限制为仅感兴趣的列。

      az ad sp list \
          --filter "startswith(displayName, 'msdocs')" \
          --query "[].{objectId:objectId, displayName:displayName}" \
          --output table
      

      然后,可以使用 az ad group member add 命令将成员添加到组。

      az ad group member add \
          --group \<group-name> \
          --member-id \<object-id>  \
      

      3 - 将角色分配给应用程序

      接下来,需要确定应用需要哪些角色) 哪些 (角色) 哪些资源并将这些角色分配给应用。 在此示例中,角色将分配给在步骤 2 中创建的 Azure Active Directory 组。 可以在资源、资源组或订阅范围内分配角色。 此示例演示如何在资源组范围内分配角色,因为大多数应用程序将其所有 Azure 资源分组到单个资源组中。

      Azure 门户 Azure CLI“选择成员 ”对话框中:
      1. 选择” 文本框可用于筛选订阅中的用户和组列表。 如果需要,请键入为应用创建的本地开发 Azure AD 组的前几个字符。
      2. 选择与应用程序关联的本地开发 Azure AD 组。
      3. 选择对话框底部的 “选择 ”以继续。

        应用程序服务主体使用 az role assignment create 命令在 Azure 中分配角色。

        az role assignment create --assignee "{appId}" \
            --scope /subscriptions/"{subscriptionName}" \
            --role "{roleName}" \
            --resource-group "{resourceGroupName}"
        

        若要获取服务主体可分配到的角色名称,请使用 az role definition list 命令。

        az role definition list \
            --query "sort_by([].{roleName:roleName, description:description}, &roleName)" \
            --output table
        

        例如,若要允许具有 appId 00000000-0000-0000-0000-000000000000 的应用程序服务主体对 msdocs-python-sdk-auth-example 资源组中的所有存储帐户Azure 存储 blob 容器和数据的访问权限,可以使用以下命令将应用程序服务主体分配给存储 Blob 数据参与者角色。

        az role assignment create --assignee "00000000-0000-0000-0000-000000000000" \
            --scope /subscriptions/"Storage Blob Data Subscriber" \
            --role "Storage Blob Data Contributor" \
            --resource-group "msdocs-python-sdk-auth-example"
        

        有关使用 Azure CLI 在资源或订阅级别分配权限的信息,请参阅文章 :使用 Azure CLI 分配 Azure 角色

        4 - 设置本地开发环境变量

        对象 DefaultAzureCredential 将在运行时在一组环境变量中查找服务主体信息。 由于大多数开发人员处理多个应用程序,因此建议使用 python-dotenv 等包从 .env 开发过程中存储在应用程序目录中的文件访问环境。 这会限定用于向 Azure 对应用程序进行身份验证的环境变量的范围,以便它们只能由此应用程序使用。

        文件 .env 从不签入源代码管理,因为它包含 Azure 的应用程序密钥。 Python 的标准 .gitignore 文件会自动从签入中排除 .env 该文件。

        若要使用 python-dotenv 包,请先在应用程序中安装该包。

        pip install python-dotenv
        

        然后,在应用程序根目录中创建文件 .env 。 使用从应用注册过程获取的值设置环境变量值,如下所示:

      4. AZURE_CLIENT_ID →应用 ID 值。
      5. AZURE_TENANT_ID →租户 ID 值。
      6. AZURE_CLIENT_SECRET →为应用生成的密码/凭据。
      7. AZURE_CLIENT_ID=00000000-0000-0000-0000-000000000000
        AZURE_TENANT_ID=11111111-1111-1111-1111-111111111111
        AZURE_CLIENT_SECRET=abcdefghijklmnopqrstuvwxyz
        

        最后,在应用程序的启动代码中,使用 python-dotenv 库从启动时的文件 .env 读取环境变量。

        from dotenv import load_dotenv
        if ( os.environ['ENVIRONMENT'] == 'development'):
            print("Loading environment variables from .env file")
            load_dotenv(".env")
        

        5 - 在应用程序中实现 DefaultAzureCredential

        若要对 Azure SDK 客户端对象进行身份验证,应用程序应使用 DefaultAzureCredential 包中的 azure.identity 类。 在此方案中, DefaultAzureCredential 将检测环境变量 AZURE_CLIENT_IDAZURE_TENANT_IDAZURE_CLIENT_SECRET 设置并读取这些变量,以获取要连接到 Azure 的应用程序服务主体信息。

        首先,将 azure.identity 包添加到应用程序。

        pip install azure-identity
        

        接下来,对于在应用中创建 Azure SDK 客户端对象的任何 Python 代码,需要:

      8. DefaultAzureCredentialazure.identity模块导入类。
      9. 创建 DefaultAzureCredential 对象。
      10. DefaultAzureCredential 对象传递给 Azure SDK 客户端对象构造函数。
      11. 以下代码段显示了此示例。

        from azure.identity import DefaultAzureCredential
        from azure.storage.blob import BlobServiceClient
        # Acquire a credential object
        token_credential = DefaultAzureCredential()
        blob_service_client = BlobServiceClient(
                account_url="https://<my_account_name>.blob.core.windows.net",
                credential=token_credential)