使用 MSAL 为用户获取 Azure AD 令牌

可使用 Microsoft 身份验证库 (MSAL) 以编程方式获取 Azure Active Directory (Azure AD) 访问令牌。 本文通过 Python 示例介绍了 MSAL 库的基本用法以及所需的用户输入。

注意

MSAL 取代了 Azure Active Directory 身份验证库 (ADAL)。 Microsoft 对 ADAL 的所有支持和开发(包括安全修补程序)已在 2022 年 6 月 30 日结束。 请参阅将应用程序迁移到 Microsoft 身份验证库 (MSAL)

提示

你可能想要尝试使用 Azure CLI 而不是 MSAL 来为用户获取 Azure AD 令牌,因为使用 Azure CLI 所涉及的步骤更少。 请参阅使用 Azure CLI 为用户获取 Azure AD 令牌

你还可在 Azure Active Directory 中定义服务主体,并为该服务主体(而不是用户)获取 Azure AD 访问令牌。 请参阅为服务主体获取 Azure AD 令牌

在 Azure 门户中配置应用

在 Azure 门户中向 Azure AD 终结点注册应用程序。 或者,你可使用已注册的 Azure AD 应用。 有关详细信息,请参阅使用 Azure 门户注册应用

  1. 登录 Azure 门户

    注意

    要使用的门户根据 Azure AD 应用程序是在 Azure 公有云中运行还是在国家云或主权云中运行而异。 有关详细信息,请参阅国家云

  2. 如果有权访问多个租户、订阅或目录,请单击顶部菜单中的“目录 + 订阅”(带筛选器的目录)图标,以切换到要注册应用程序的目录。

  3. 搜索并选择“Azure Active Directory” 。

  4. 在“管理”中,选择“应用注册”>“新建注册”。

  5. 对于“名称”,请输入应用程序的名称。

  6. 在“支持的帐户类型”部分中,选择“仅组织目录中的帐户(单一租户)”。

  7. 在“重定向 URI (可选)”部分中,为“选择平台”选择“公共客户端/本机(移动和桌面)”,并输入重定向 URI。 在下面的示例中,重定向 URI 值为 http://localhost

    注册应用

  8. 单击“注册”。

  9. 在应用程序页的“概述”页上的“概要”部分中,复制以下值:

    • 应用程序(客户端) ID
    • 目录(租户)ID
    • 在“重定向 URI”中,为之前在此过程中输入的公共客户端重定向 URI。

    关于 Azure 已注册的应用的概述

  10. 向 AzureDatabricks 添加已注册应用程序的必需权限。 只有管理员用户可执行此步骤。 如果在执行此操作时遇到与权限相关的问题,请与管理员联系以寻求帮助。

    1. 在应用程序页的“概述”页上,在“开始使用”选项卡上单击“查看 API 权限”。

      Azure 已注册的应用的设置

    2. 单击“添加权限”。

      向应用添加所需权限

    3. 在“请求 API 权限”窗格中,单击“我的组织使用的 API”选项卡,搜索并选择“AzureDatabricks”。

      添加 AzureDatabricks API 权限

    4. 启用“user_impersonation”复选框,然后单击“添加权限”。

      Azure 应用委派的权限

    5. 单击“向 ### 授予管理员同意”,然后单击“是” 。 若要执行此步骤,必须具有管理员用户身份或有权向应用程序授予同意。 如果未看到“为 ### 授予管理员同意”,或者跳过此操作,则必须在首次使用应用程序提供同意时使用授权代码流(交互式)。 之后便可使用用户名-密码流(编程式)方法。

      向应用权限添加其他用户和组

可将其他用户添加到应用程序。 有关详细信息,请参阅向企业应用程序分配用户帐户中的 Azure 门户说明或向 Azure Active Directory 中的应用程序分配用户和组中的 PowerShell 说明。 用户如果没有所需的权限,则无法获取令牌。

获取 Azure AD 访问令牌

若要获取 Azure AD 访问令牌,可以使用:

如果出现以下情况,必须使用授权代码流(交互式)来获取 Azure AD 访问令牌:

  • 在 Azure AD 中启用了双重身份验证。
  • 在 Azure AD 中启用了联合身份验证。
  • 在应用程序注册期间,不会向你授予对已注册的应用程序的同意。

如果你有权使用用户名和密码登录,则可使用用户名-密码流(编程式)获取 Azure AD 访问令牌。

授权代码流(交互式)

可通过两个步骤使用授权代码流来获取 Azure AD 访问令牌。

  1. 请求授权代码,该代码会启动一个浏览器窗口并请求 Azure 用户登录。 用户成功登录后,将返回授权代码。
  2. 使用授权代码获取 Azure AD 访问令牌。 根据所使用的方法,还可以同时返回刷新令牌,该令牌可用于刷新 Azure AD 访问令牌。

完成这两个步骤的一个方法是使用 Web 浏览器和 curl。 为此,请使用 Web 浏览器获取授权代码,然后使用该授权代码和 curl 获取 Azure AD 令牌。 此方法不提供刷新令牌。

另一种方法是使用 MSAL Python 库。 为此,请运行单个脚本,该脚本使用 Web 浏览器获取授权代码,然后使用授权代码获取访问令牌令牌和刷新令牌。

这两种方法都假定你已登录到 Azure。 如果未登录,Web 浏览器会提示你登录。

使用 Web 浏览器和 curl 获取 Azure AD 令牌

  1. 收集以下信息:

    参数 说明
    租户 ID 在 Azure 门户中配置应用时在 Azure AD 中注册的应用程序的目录(租户)ID。
    客户端 ID 在 Azure AD 中注册的应用程序的“应用程序(客户端)ID”。
    重定向 URI 在 Azure AD 中注册的应用程序的相应重定向 URI(例如 http://localhost)。 会向此 URI 发送身份验证响应(包含授权代码)。
  2. 使用 Web 浏览器浏览到以下 URL,获取授权代码。 请相应地替换以下 URL 示例中的字段。 请注意,URL 必须作为单行发送;为了便于阅读,已将换行符添加到以下 URL。 有关详细信息,请参阅请求授权代码

    https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/authorize?client_id=<client-id>
    &response_type=code
    &redirect_uri=<redirect-uri>
    &response_mode=query
    &scope=2ff814a6-3304-4ab8-85cb-cd0e6f879c1d%2F.default
    &state=<state>
    

    将:

    • <tenant-id> 替换为已注册应用程序的租户 ID。
    • <client-id> 替换为已注册应用程序的客户端 ID。
    • <redirect-uri> 替换为已注册应用程序的重定向 URI。 此 URI 必须采用 URL 编码(百分比编码)格式。 例如,http://localhosthttp%3A%2F%2Flocalhost
    • <state> 替换为一个随机数或一些编码信息。 为了帮助检查信息交换的完整性,此状态值应与此过程稍后返回的 URL 中的值匹配。

    不要更改 scope 参数的值。 它表示 Azure Databricks 的编程 ID(2ff814a6-3304-4ab8-85cb-cd0e6f879c1d),以及默认作用域(/.default,URL 编码为 %2f.default)。

    例如:

    https://login.microsoftonline.com/a1bc2d34-5e67-8f89-01ab-c2345d6c78de/oauth2/v2.0/authorize?client_id=12a34b56-789c-0d12-e3fa-b456789c0123
    &response_type=code
    &redirect_uri=http%3A%2F%2Flocalhost
    &response_mode=query
    &scope=2ff814a6-3304-4ab8-85cb-cd0e6f879c1d%2F.default
    &state=12345
    
  3. 将 URL 作为单行粘贴到 Web 浏览器中,并在出现提示时登录到 Azure。

    HTTP 请求 URL

  4. 授权代码位于返回 URL 的 code 字段中。 将授权代码保存在安全的位置。 此外,进行检查以确保 state 字段的值与之前在此过程中提供的值匹配。

    授权代码 URL

    完整的返回 URL 将如下所示(为简洁起见,此处将完整的 code 字段值缩短为 0.ASkAIj...RxgFhSAA):

    http://localhost/?code=0.ASkAIj...RxgFhSAA&state=12345&session_state=c44574d5-38ba-4f93-b2a3-a830db8e8cdf
    
  5. 使用授权代码以及 curl 来获取 Azure AD 访问令牌。

    curl -X POST -H 'Content-Type: application/x-www-form-urlencoded' \
    https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token \
    -d 'client_id=<client-id>' \
    -d 'scope=2ff814a6-3304-4ab8-85cb-cd0e6f879c1d%2F.default' \
    -d 'code=<authorization-code>' \
    -d 'redirect_uri=<redirect-uri>' \
    -d 'grant_type=authorization_code' \
    -d 'state=<state>'
    

    将:

    • <tenant-id> 替换为已注册应用程序的租户 ID。
    • <client-id> 替换为已注册应用程序的客户端 ID。
    • <authorization-code> 替换为授权代码。
    • <redirect-uri> 替换为已注册应用程序的重定向 URI。 此 URI 必须采用 URL 编码(百分比编码)格式。 例如,http://localhosthttp%3A%2F%2Flocalhost
    • <state> 替换为一个随机数或一些编码信息。 为了帮助检查信息交换的完整性,此状态值应与此过程稍后的响应有效负载中的值匹配。

    不要更改 scope 参数的值。 它表示 Azure Databricks 的编程 ID(2ff814a6-3304-4ab8-85cb-cd0e6f879c1d),以及默认作用域(/.default,URL 编码为 %2f.default)。

    例如:

    curl -X POST -H 'Content-Type: application/x-www-form-urlencoded' \
    https://login.microsoftonline.com/a1bc2d34-5e67-8f89-01ab-c2345d6c78de/oauth2/v2.0/token \
    -d 'client_id=12a34b56-789c-0d12-e3fa-b456789c0123' \
    -d 'scope=2ff814a6-3304-4ab8-85cb-cd0e6f879c1d%2F.default' \
    -d 'code=0.ASkAIj...RxgFhSAA' \
    -d 'redirect_uri=http%3A%2F%2Flocalhost' \
    -d 'grant_type=authorization_code' \
    -d 'state=12345'
    

    Azure AD 令牌位于调用结果内的 access_token 值中。 请务必检查 state 值是否与之前在此过程中提供的值匹配。

使用 MSAL Python 库获取 Azure AD 令牌

  1. 收集以下信息:

    参数 说明
    租户 ID 在 Azure 门户中配置应用时在 Azure AD 中注册的应用程序的目录(租户)ID。
    客户端 ID 在 Azure AD 中注册的应用程序的“应用程序(客户端)ID”。

    此过程假设你已将 http://localhost 设置为在 Azure AD 中注册的应用程序的重定向 URI。

  2. 通过运行 pip install msal 在本地计算机上安装 MSAL Python SDK。

  3. 在本地计算机上将以下代码保存为 get-tokens.py

    # Given the client ID and tenant ID for an app registered in Azure,
    # provide an Azure AD access token and a refresh token.
    
    # If the caller is not already signed in to Azure, the caller's
    # web browser will prompt the caller to sign in first.
    
    # pip install msal
    from msal import PublicClientApplication
    import sys
    
    # You can hard-code the registered app's client ID and tenant ID here,
    # or you can provide them as command-line arguments to this script.
    client_id = '<client-id>'
    tenant_id = '<tenant-id>'
    
    # Do not modify this variable. It represents the programmatic ID for
    # Azure Databricks along with the default scope of '/.default'.
    scopes = [ '2ff814a6-3304-4ab8-85cb-cd0e6f879c1d/.default' ]
    
    # Check for too few or too many command-line arguments.
    if (len(sys.argv) > 1) and (len(sys.argv) != 3):
      print("Usage: get-tokens.py <client ID> <tenant ID>")
      exit(1)
    
    # If the registered app's client ID and tenant ID are provided as
    # command-line variables, set them here.
    if len(sys.argv) > 1:
      client_id = sys.argv[1]
      tenant_id = sys.argv[2]
    
    app = PublicClientApplication(
      client_id = client_id,
      authority = "https://login.microsoftonline.com/" + tenant_id
    )
    
    acquire_tokens_result = app.acquire_token_interactive(
      scopes = scopes
    )
    
    if 'error' in acquire_tokens_result:
      print("Error: " + acquire_tokens_result['error'])
      print("Description: " + acquire_tokens_result['error_description'])
    else:
      print("Access token:\n")
      print(acquire_tokens_result['access_token'])
      print("\nRefresh token:\n")
      print(acquire_tokens_result['refresh_token'])
    
  4. 执行下列操作之一:

    • 在上面的代码中,将 <client-id> 替换为已注册应用程序的客户端 ID,将 <tenant-id> 替换为已注册应用程序的租户 ID,然后运行脚本(例如 python get-tokens.py)。
    • 在运行脚本(例如 python get-tokens.py 12a34b56-789c-0d12-e3fa-b456789c0123 a1bc2d34-5e67-8f89-01ab-c2345d6c78de)时,提供已注册应用程序的客户端 ID 和已注册应用程序的租户 ID。
  5. 如果 Web 浏览器发出提示,请登录到 Azure。

  6. 将在输出中显示 Azure AD 访问令牌和刷新令牌。

用户名-密码流(编程式)

  1. 如果你有权使用用户名和密码登录,请收集以下信息:

    参数 说明
    租户 ID 在 Azure 门户中配置应用时在 Azure AD 中注册的应用程序的目录(租户)ID。
    客户端 ID 在 Azure AD 中注册的应用程序的“应用程序(客户端)ID”。
    用户名和密码 租户中用户的用户名(即登录到 Azure 门户时的电子邮件地址)和密码。

    此过程假设你已将 http://localhost 设置为在 Azure AD 中注册的应用程序的重定向 URI。

  2. 通过运行 pip install msal 在本地计算机上安装 MSAL Python SDK。

  3. 在本地计算机上将以下代码保存为 get-tokens-for-user.py

    # Given the client ID and tenant ID for an app registered in Azure,
    # along with an Azure username and password,
    # provide an Azure AD access token and a refresh token.
    
    # If the caller is not already signed in to Azure, the caller's
    # web browser will prompt the caller to sign in first.
    
    # pip install msal
    from msal import PublicClientApplication
    import sys
    
    # You can hard-code the registered app's client ID and tenant ID here,
    # along with the Azure username and password,
    # or you can provide them as command-line arguments to this script.
    client_id = '<client-id>'
    tenant_id = '<tenant-id>'
    username = '<username>'
    password = '<password>'
    
    # Do not modify this variable. It represents the programmatic ID for
    # Azure Databricks along with the default scope of '/.default'.
    scope = [ '2ff814a6-3304-4ab8-85cb-cd0e6f879c1d/.default' ]
    
    # Check for too few or too many command-line arguments.
    if (len(sys.argv) > 1) and (len(sys.argv) != 5):
      print("Usage: get-tokens-for-user.py <client ID> <tenant ID> <username> <password>")
      exit(1)
    
    # If the registered app's client ID and tenant ID along with the
    # Azure username and password are provided as command-line variables,
    # set them here.
    if len(sys.argv) > 1:
      client_id = sys.argv[1]
      tenant_id = sys.argv[2]
      username = sys.argv[3]
      password = sys.argv[4]
    
    app = PublicClientApplication(
      client_id = client_id,
      authority = "https://login.microsoftonline.com/" + tenant_id
    )
    
    acquire_tokens_result = app.acquire_token_by_username_password(
      username = username,
      password = password,
      scopes = scope
    )
    
    if 'error' in acquire_tokens_result:
      print("Error: " + acquire_tokens_result['error'])
      print("Description: " + acquire_tokens_result['error_description'])
    else:
      print("Access token:\n")
      print(acquire_tokens_result['access_token'])
      print("\nRefresh token:\n")
      print(acquire_tokens_result['refresh_token'])
    
  4. 执行下列操作之一:

    • 在上面的代码中,将 <client-id> 替换为已注册应用程序的客户端 ID,将 <tenant-id> 替换为已注册应用程序的租户 ID,将 <username> 替换为用户名,并将 <password> 替换为密码,然后运行脚本(例如 python get-tokens-for-user.py)。
    • 在运行脚本(例如 python get-tokens-for-user.py 12a34b56-789c-0d12-e3fa-b456789c0123 a1bc2d34-5e67-8f89-01ab-c2345d6c78de someone@example.com "MyPa55w&rd!")时,提供已注册应用程序的客户端 ID、已注册应用程序的租户 ID、用户名和密码。 如果命令行参数包含特殊字符,则应为其加上引号。
  5. 将在终端中显示 Azure AD 访问令牌和刷新令牌。

使用 Azure AD 访问令牌访问 Databricks REST API

本部分介绍如何使用 Azure AD 访问令牌调用 Databricks REST API。 在以下示例中,请将 <access-token> 替换为 Azure AD 访问令牌,并将 <databricks-instance> 替换为 Azure Databricks 部署的每工作区 URL

Python 示例

此示例演示如何列出 Azure Databricks 工作区中的群集。

import requests
import json

databricks_instance = '<databricks-instance>'
api_version = '/api/2.0'
api_command = '/clusters/list'
url = f"https://{databricks_instance}{api_version}{api_command}"
access_token = '<access-token>'

response = requests.get(
  url = url,
  headers = { 'Authorization': "Bearer " + access_token}
)

print(json.dumps(json.loads(response.text), indent = 2))

注意

如果你不是管理员用户,但想以管理员用户身份登录,则除了 'Authorization' : 'Bearer ' 标头外,还必须提供 标头,并且对于 Azure 中的工作区资源,你必须是参与者或所有者角色。 按如下所示构造 X-Databricks-Azure-Workspace-Resource-Id 值:

# ...

subscription = '<azure-subscription-id>'
resource_group = '<azure-resource-group-name>'
workspace = '<databricks-workspace-name-in-azure>'

db_resource_id = '/subscriptions/%s/resourcegroups/%s/providers/microsoft.databricks/workspaces/%s' % (
  subscription,
  resource_group,
  workspace
)

# ...

  headers = {
    'Authorization': "Bearer " + access_token,
    'X-Databricks-Azure-Workspace-Resource-Id': db_resource_id
  }

# ...

若要在 Azure 中获取订阅、资源和工作区信息,请参阅 打开资源。 要打开目标资源,可以在 Azure 中搜索 Azure Databricks 服务类型以及你了解的有关目标 Azure Databricks 工作区的任何其他信息。

curl 实例

curl -X GET \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <access-token>' \
https://<databricks-instance>/api/2.0/clusters/list

刷新 Azure AD 访问令牌

如果你获取了刷新令牌和 Azure AD 访问令牌,可使用刷新令牌来获取新的令牌。 默认情况下,Azure AD 访问令牌的生存期为 60 到 90 分钟之间的随机时长(平均 75 分钟)。 可使用 Azure Active Directory 中的可配置令牌生存期中的方法配置 Azure AD 访问令牌的生存期。

下面的示例演示如何使用 MSAL Python 库以及刷新令牌来获取新令牌。

  1. 在本地计算机上将以下代码保存为 refresh-tokens.py

    # Given the client ID and tenant ID for an app registered in Azure,
    # along with a refresh token, provide a new Azure AD access token and
    # refresh token.
    
    # If the caller is not already signed in to Azure, the caller's
    # web browser will prompt the caller to sign in first.
    
    # pip install msal
    from msal import PublicClientApplication
    import sys
    
    # You can hard-code the registered app's client ID, tenant ID,
    # and refresh token here, or you can provide them as command-line
    # arguments to this script.
    client_id = '<client-id>'
    tenant_id = '<refresh-token'
    refresh_token = '<refresh-token>'
    
    # Do not modify this variable. It represents the programmatic ID for
    # Azure Databricks along with the default scope of '.default'.
    scope = [ '2ff814a6-3304-4ab8-85cb-cd0e6f879c1d/.default' ]
    
    # Check for too few or too many command-line arguments.
    if (len(sys.argv) > 1) and (len(sys.argv) != 4):
      print("Usage: refresh-tokens.py <client ID> <tenant ID> <refresh token>")
      exit(1)
    
    # If the registered app's client ID, tenant ID, and refresh token are
    # provided as command-line variables, set them here.
    if len(sys.argv) > 1:
      client_id = sys.argv[1]
      tenant_id = sys.argv[2]
      refresh_token = sys.argv[3]
    
    app = PublicClientApplication(
      client_id = client_id,
      authority = "https://login.microsoftonline.com/" + tenant_id
    )
    
    acquire_tokens_result = app.acquire_token_by_refresh_token(
      refresh_token = refresh_token,
      scopes = scope
    )
    
    if 'error' in acquire_tokens_result:
      print("Error: " + acquire_tokens_result['error'])
      print("Description: " + acquire_tokens_result['error_description'])
    else:
      print("\nNew access token:\n")
      print(acquire_tokens_result['access_token'])
      print("\nNew refresh token:\n")
      print(acquire_tokens_result['refresh_token'])
    
  2. 执行下列操作之一:

    • 在上面的代码中,将 <client-id> 替换为已注册应用程序的客户端 ID,将 <tenant-id> 替换为已注册应用程序的租户 ID,并将 <refresh-token> 替换为刷新令牌,然后运行脚本(例如 python get-tokens-for-user.py)。
    • 在运行脚本(例如 python refresh-tokens.py 12a34b56-789c-0d12-e3fa-b456789c0123 a1bc2d34-5e67-8f89-01ab-c2345d6c78de "0.ASkAIj...huE84ALg")时,提供已注册应用程序的客户端 ID、已注册应用程序的租户 ID 和刷新令牌。 如果命令行参数包含特殊字符,则应为其加上引号。
  3. 将在终端中显示新的 Azure AD 访问令牌和刷新令牌。