手动获取 Microsoft Entra ID 令牌

本页介绍如何为用户和服务主体手动生成 Microsoft Entra ID 访问令牌,以便使用 Azure Databricks REST API 进行身份验证。 手动令牌生成是一种高级技术。

重要

Databricks 不建议手动创建Microsoft Entra ID 令牌。 它们将在一小时内过期,需要手动更换。 而是使用具有 统一身份验证 的工具或 SDK 自动处理令牌管理。

对大多数用例使用 Azure Databricks 托管服务主体 。 仅在需要访问 Azure 资源时才使用 Microsoft Entra ID 托管服务主体,因为这需要额外的 Microsoft Entra ID 权限。

何时使用手动令牌生成

仅在以下情况下使用手动令牌生成:

  • 需要与无法使用 Azure CLI 或统一身份验证的系统集成
  • 你需要显式地控制令牌的生命周期和刷新频率。
  • 正在调试身份验证问题

对于所有其他用例,请使用自动处理令牌管理的推荐身份验证方法。

获取用户的令牌

使用 Azure CLI 或Microsoft身份验证库(MSAL)获取用户的Microsoft Entra ID 访问令牌。

Azure CLI 方法

  1. 获取用户帐户的 Azure 订阅 ID:

    • 在 Azure Databricks 工作区中: 单击用户名 >Azure 门户>概述,找到 订阅 ID

    • 使用 Azure CLI: 运行以下命令(替换为工作区 URL):

      az databricks workspace list --query "[?workspaceUrl==\`adb-0000000000000000.0.azuredatabricks.net\`].{id:id}" -o tsv
      
      # /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/my-rg/providers/Microsoft.Databricks/workspaces/my-ws
      

      00000000-0000-0000-0000-000000000000 之后的 /subscriptions/ 是您的订阅 ID。

      如果出现租户错误,请登录正确的租户:

      az login -t <tenant-id>
      

      请参阅 Azure 门户中的“获取订阅和租户 ID”。

  2. 登录到 Azure:

    az login
    
  3. 设置正确的订阅:

    az account set -s <subscription-id>
    
  4. 生成访问令牌:

    az account get-access-token \
    --resource 2ff814a6-3304-4ab8-85cb-cd0e6f879c1d \
    --query "accessToken" \
    -o tsv
    

    资源 ID 2ff814a6-3304-4ab8-85cb-cd0e6f879c1d 是所有 Azure 环境中 Azure Databricks 的标准标识符。

MSAL 方法

使用 Microsoft身份验证库(MSAL) 以编程方式获取Microsoft Entra ID 访问令牌。 MSAL 支持两个流程:

  • 授权代码流(交互式): 启动用于用户登录的浏览器。 启用双重身份验证或联合身份验证时,或者需要管理员同意时,请使用此功能。
  • 用户名密码流(编程): 使用用户名和密码进行身份验证。 仅当具有以编程方式登录的权限时,才使用此功能。

在使用 MSAL 之前,必须在 Microsoft Entra ID 中注册应用程序。 请参阅 使用 Azure 门户注册应用。 注册时:

  1. 仅将支持的帐户类型设置为此组织目录中的帐户(单租户)。
  2. 设置 重定向 URI公共客户端/本机(移动和桌面),值为 http://localhost
  3. 记下应用的“概述”页中的应用程序(客户端)ID目录(租户)ID
  4. 添加 AzureDatabricks API 权限:
    1. 转到 API 权限>添加权限
    2. 搜索并选择 AzureDatabricks
    3. 启用 user_impersonation 并单击“ 添加权限”。
    4. 单击“ 授予管理员许可 ”(需要管理员权限)。

授权代码流

安装 MSAL Python SDK:

pip install msal

将以下代码另存为 get-tokens.py

from msal import PublicClientApplication
import sys

# Provide client ID and tenant ID as command-line arguments
client_id = sys.argv[1] if len(sys.argv) > 1 else '<client-id>'
tenant_id = sys.argv[2] if len(sys.argv) > 1 else '<tenant-id>'

scopes = ['2ff814a6-3304-4ab8-85cb-cd0e6f879c1d/.default']

app = PublicClientApplication(
  client_id=client_id,
  authority=f"https://login.microsoftonline.com/{tenant_id}"
)

result = app.acquire_token_interactive(scopes=scopes)

if 'error' in result:
  print(f"Error: {result['error']}")
  print(f"Description: {result['error_description']}")
else:
  print(f"Access token:\n{result['access_token']}")
  print(f"\nRefresh token:\n{result['refresh_token']}")

运行脚本:

python get-tokens.py <client-id> <tenant-id>

用户名-密码流

将以下代码另存为 get-tokens-user.py

from msal import PublicClientApplication
import sys

client_id = sys.argv[1] if len(sys.argv) > 1 else '<client-id>'
tenant_id = sys.argv[2] if len(sys.argv) > 1 else '<tenant-id>'
username = sys.argv[3] if len(sys.argv) > 1 else '<username>'
password = sys.argv[4] if len(sys.argv) > 1 else '<password>'

scopes = ['2ff814a6-3304-4ab8-85cb-cd0e6f879c1d/.default']

app = PublicClientApplication(
  client_id=client_id,
  authority=f"https://login.microsoftonline.com/{tenant_id}"
)

result = app.acquire_token_by_username_password(
  username=username,
  password=password,
  scopes=scopes
)

if 'error' in result:
  print(f"Error: {result['error']}")
  print(f"Description: {result['error_description']}")
else:
  print(f"Access token:\n{result['access_token']}")
  print(f"\nRefresh token:\n{result['refresh_token']}")

运行脚本:

python get-tokens-user.py <client-id> <tenant-id> <username> <password>

获取服务主体的令牌

服务主体使用 OAuth 2.0 客户端凭据流 ,并且可以具有与常规用户不同的访问控制。

创建服务主体

如果没有服务主体,请使用 Azure 门户或 Azure CLI 创建一个服务主体:

Azure 门户

  1. 登录到 Azure 门户
  2. 如果需要,请使用 目录 + 订阅 切换到正确的租户。
  3. 搜索并选择 Microsoft Entra ID
  4. 单击“ + 添加>应用注册”。
  5. 输入名称并选择“仅此组织目录中的帐户”(单租户)。
  6. 单击“注册”。
  7. “概述 ”页复制这些值:
    • 应用程序(客户端)ID
    • 目录(租户)ID
  8. 转到 证书和机密>客户端机密>“新建客户端机密”。
  9. 添加说明,设置到期期限,然后单击“ 添加”。
  10. 复制并安全地存储客户端机密

Azure CLI

有关完整说明,请参阅 使用 Azure CLI 创建 Azure 服务主体

生成令牌

使用 REST API 或 Azure CLI 生成令牌。 首先收集以下信息:

  • 租户 ID:Microsoft Entra ID 中的目录(租户)ID
  • 客户端 ID:Microsoft Entra ID 中的应用程序(客户端)ID
  • 客户端密码:来自 Microsoft Entra ID 的客户端机密

REST API 方法

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 'grant_type=client_credentials' \
-d 'scope=2ff814a6-3304-4ab8-85cb-cd0e6f879c1d%2F.default' \
-d 'client_secret=<client-secret>'

访问令牌位于 access_token 响应的字段中。

Azure CLI 方法

  1. 获取 Azure 订阅 ID。 请参阅 获取用户的令牌

  2. 使用服务主体登录:

    az login \
    --service-principal \
    -t <tenant-id> \
    -u <client-id> \
    -p <client-secret>
    
  3. 设置正确的订阅:

    az account set -s <subscription-id>
    
  4. 生成访问令牌:

    az account get-access-token \
    --resource 2ff814a6-3304-4ab8-85cb-cd0e6f879c1d \
    --query "accessToken" \
    -o tsv
    

将令牌与 Databricks API 配合使用

生成令牌后,将其与 Databricks REST API 一起使用。

工作区中的服务主体

如果您的服务主体已使用 服务主体 APIDatabricks CLI 添加到工作区:

Databricks 命令行界面 (CLI)

databricks clusters list -p <profile-name-with-token>

curl

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

具有 Azure 角色的服务主体

如果服务主体在 Azure 中的工作区资源上具有 ContributorOwner 角色,但尚不在 Azure Databricks 工作区中:

  1. 获取 Azure 资源管理器的管理令牌:

    curl -X POST -H 'Content-Type: application/x-www-form-urlencoded' \
    https://login.microsoftonline.com/<tenant-id>/oauth2/token \
    -d 'client_id=<client-id>' \
    -d 'grant_type=client_credentials' \
    -d 'resource=https%3A%2F%2Fmanagement.core.windows.net%2F' \
    -d 'client_secret=<client-secret>'
    
  2. 使用两个令牌调用 Databricks API:

    curl -X GET \
    -H 'Authorization: Bearer <databricks-access-token>' \
    -H 'X-Databricks-Azure-SP-Management-Token: <management-access-token>' \
    -H 'X-Databricks-Azure-Workspace-Resource-Id: /subscriptions/<subscription-id>/resourceGroups/<resource-group-name>/providers/Microsoft.Databricks/workspaces/<workspace-name>' \
    https://<databricks-instance>/api/2.0/clusters/list
    

首次身份验证后,服务主体将成为工作区管理员。

刷新令牌

如果使用访问令牌获取了刷新令牌,请使用它获取新令牌。 默认情况下,Microsoft Entra ID 访问令牌在 60-90 分钟后过期。

将以下代码另存为 refresh-tokens.py

from msal import PublicClientApplication
import sys

client_id = sys.argv[1] if len(sys.argv) > 1 else '<client-id>'
tenant_id = sys.argv[2] if len(sys.argv) > 1 else '<tenant-id>'
refresh_token = sys.argv[3] if len(sys.argv) > 1 else '<refresh-token>'

scopes = ['2ff814a6-3304-4ab8-85cb-cd0e6f879c1d/.default']

app = PublicClientApplication(
  client_id=client_id,
  authority=f"https://login.microsoftonline.com/{tenant_id}"
)

result = app.acquire_token_by_refresh_token(
  refresh_token=refresh_token,
  scopes=scopes
)

if 'error' in result:
  print(f"Error: {result['error']}")
  print(f"Description: {result['error_description']}")
else:
  print(f"\nNew access token:\n{result['access_token']}")
  print(f"\nNew refresh token:\n{result['refresh_token']}")

运行脚本:

python refresh-tokens.py <client-id> <tenant-id> <refresh-token>

排查令牌问题

本部分介绍常见的令牌错误以及如何验证访问令牌。

未能使用用户名和密码获取令牌

The user or administrator has not consented to use the application with ID <client-id>.
Send an interactive authorization request for this user and resource.

解决方案: 确保将 AzureDatabricks 资源添加到您的应用程序中。 使用授权代码流程(互动方式)授权许可。 同意后,可以使用用户名和密码认证流程。 请参阅 获取用户的令牌

重定向 URI 不匹配

The reply URL specified in the request does not match the reply URLs configured for the application: '<application-id>'

解决方案: 验证请求中的重定向 URI 是否与应用程序中配置的 URI 匹配。

验证访问令牌

请确保Microsoft Entra ID 访问令牌包含正确的信息。 请参阅 验证令牌

  • aud2ff814a6-3304-4ab8-85cb-cd0e6f879c1d (Databricks 资源 ID)
  • isshttps://sts.windows.net/<tenant-id>/
  • tid:工作区租户 ID
  • nbf/exp:当前时间介于这些值之间
  • unique_name:工作区中存在用户

使用 OIDC 终结点中的公共证书验证签名。

在没有签名验证的情况下解码:

以下代码显示了令牌的有效负载。 必须首先使用 安装 pip install pyjwt 库,并使用 pip install cryptography 安装加密库。

import jwt

def decode_token(token):
  algorithm = jwt.get_unverified_header(token)['alg']
  decoded = jwt.decode(token, algorithms=[algorithm], options={"verify_signature": False})
  for key in decoded.keys():
    print(f"{key}: {str(decoded[key])}")

使用签名验证进行解码:

import jwt
import requests
from cryptography.x509 import load_pem_x509_certificate
from cryptography.hazmat.backends import default_backend

PEMSTART = '-----BEGIN CERTIFICATE-----\n'
PEMEND = '\n-----END CERTIFICATE-----\n'

def get_public_key_for_token(kid):
  response = requests.get('https://login.microsoftonline.com/common/.well-known/openid-configuration').json()
  pubkeys = requests.get(response['jwks_uri']).json()['keys']

  for key in pubkeys:
    if key['kid'] == kid:
      cert_str = PEMSTART + str(key['x5c'][0]) + PEMEND
      cert_obj = load_pem_x509_certificate(bytes(cert_str, 'ascii'), default_backend())
      return cert_obj.public_key()

def aad_access_token_decoder(access_token):
  header = jwt.get_unverified_header(access_token)
  public_key = get_public_key_for_token(header['kid'])
  decoded = jwt.decode(access_token, key=public_key, algorithms='RS256',
                      audience='2ff814a6-3304-4ab8-85cb-cd0e6f879c1d')
  for key in decoded.keys():
    print(f"{key}: {str(decoded[key])}")

对非敏感令牌使用联机 JWT 解码器,例如 jwt.msjwt.io