3、访问密钥保管库的身份认证:Azure AD认证、服务主体、托管标识、访问令牌获取

聊到 Key Vault 的安全,绕不开一个问题——「谁有资格开这把锁?」

嗯,身份认证就是这个问题的答案。Azure 这边走的是 Azure AD 路线,不管是人、是应用、还是自动化脚本,想碰 Key Vault 里的秘密,都得先过 Azure AD 这一关。今天这篇,我就把这几条主流认证路子掰开讲清楚。

3.1 Azure AD 认证:基石与思路

Key Vault 本身不存用户密码,它只认 Azure AD 颁发的令牌()。说白了,你拿到令牌就相当于拿到了临时入场券。令牌背后绑定的是一个身份,这个身份可以是:

  • 用户账号(User Principal)——开发人员登录 Azure Portal 或 CLI 时用。
  • 服务主体(Service Principal)——应用程序或 CI/CD 管道的「机器人账号」。
  • 托管标识(Managed Identity)——Azure 资源自带的免密身份。

这三种方式最终都会走到 OAuth 2.0 的客户端凭证流或授权码流,拿到 JWT 令牌,然后往 Key Vault 的 REST API 的 Authorization 头里一塞,完事。

💡 小贴士:我习惯把 Azure AD 令牌想象成「酒店房卡」。用户账号是前台人工发的,服务主体是公司配发的工卡,托管标识则是房间门锁感应到你就是住客——连卡都不用掏。

3.2 服务主体:给应用配一个「机器人户口」

服务主体的核心作用,就是让无用户交互的场景(比如后端服务、定时任务、GitHub Actions)能安全地访问 Azure 资源。创建服务主体后,你会得到一个 Client ID 和 Client Secret(或者证书),靠它们去换取令牌。

创建步骤(极简版):

  1. 在 Azure AD 中注册一个应用(App Registration)。
  2. 生成客户端密码(或上传证书)。
  3. 给这个服务主体在 Key Vault 的访问策略中分配对应权限(比如 Get, List, Set)。

用 Python 获取令牌的代码示例:

from azure.identity import ClientSecretCredential
from azure.keyvault.secrets import SecretClient

tenant_id = "your-tenant-id"
client_id = "your-client-id"
client_secret = "your-client-secret"
vault_url = "https://your-vault.vault.azure.net"

credential = ClientSecretCredential(tenant_id, client_id, client_secret)
client = SecretClient(vault_url=vault_url, credential=credential)

secret = client.get_secret("mySecret")
print(secret.value)
⚠️ 曾经踩过的坑:客户端的密钥千万别硬编码在代码里!我曾经接手过一个项目,团队把 client_secret 直接写在了配置文件的 appsettings.json 里,还上传到了公共 Git 仓库……后面的事你可想而知。至少要用环境变量或 Azure Key Vault 引用去存。

3.3 托管标识:真正的「免密」方案

托管标识(Managed Identity)是我个人最喜欢的认证方式。它让 Azure 资源(比如 VM、App Service、Functions)自动获得一个 Azure AD 身份,你根本不需要管凭据是怎么流转的。

两种类型:

类型 生命周期 典型场景
系统分配(System-assigned) 与 Azure 资源绑定,资源删掉它也跟着没 单应用单标识,简单直接
用户分配(User-assigned) 独立资源,可被多个资源共用 多应用共用一个身份,统一权限管理

内部原理:Azure 会给启用托管标识的资源创建一个对应的服务主体,然后通过内部元数据端点(169.254.169.254/metadata/identity/oauth2/)自动管理令牌的签发和刷新。你在代码里只需要用 DefaultAzureCredential,它会优先尝试托管标识的令牌获取方式。

代码示例(用托管标识访问 Key Vault):

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

credential = DefaultAzureCredential()
client = SecretClient(vault_url="https://your-vault.vault.azure.net", credential=credential)

secret = client.get_secret("mySecret")
print(secret.value)
💡 为什么我推荐托管标识?因为不需要管客户端密码过期、不需要轮换证书、更不用担心泄露。我在一个生产项目里把所有 VM 里的应用都换成了托管标识,安全团队审核时直接给了满分。

3.4 访问令牌获取:几种常见通道

不管你用哪种身份,最终都要拿到令牌才能调 Key Vault 的 API。除了 SDK 自动帮你打理,有时候你也需要手动获得令牌做测试或调试。

3.4.1 Azure CLI

# 先登录用户身份
az login

# 获取 Key Vault 令牌
az account get-access- --resource https://vault.azure.net

3.4.2 PowerShell

Connect-AzAccount
Get-AzAccessToken -ResourceUrl "https://vault.azure.net"

3.4.3 直接调用 REST API(服务主体)

POST https://login.microsoftonline.com/{tenant}/oauth2/v2.0/
Content-Type: application/x-www-form-urlencoded

client_id=xxx
&scope=https://vault.azure.net/.default
&client_secret=yyy
&grant_type=client_credentials

返回结果里的 access_ 就是你需要的。

3.5 三种认证方式对比

维度 用户账号 服务主体 托管标识
需要管理凭据吗 是,用户密码 是,客户端密码/证书 否
适用于自动化 不推荐(MFA 等问题) 完美适用 最佳适用
权限隔离粒度 按用户 按应用 按资源或共用标识
令牌刷新方式 用户交互/设备流 客户端凭证流自动刷新 Azure 基础架构自动管理
典型场景 开发调试、Portal 操作 CI/CD、后端服务 Azure 原生资源内应用

3.6 核心流程示意图

Azure Key Vault 身份认证流程 用户账号 服务主体 托管标识 Azure AD OAuth 2.0 令牌签发 访问令牌 JWT (有效期默认60分钟) KV 📌 关键说明 1. 令牌通过 Authorization: Bearer <> 头传给 Key Vault REST API。 2. 托管标识自动通过 IMDS 端点获取令牌,代码层使用 DefaultAzureCredential。 3. 服务主体的 client_secret 或证书需安全托管,推荐用 Key Vault 本身存储。

3.7 避坑指南 & 最佳实践

  • 令牌过期处理:SDK 里的 DefaultAzureCredential 会自动刷新令牌,但你如果自己写 HTTP 请求拿令牌,记得处理 401 重试和刷新逻辑。
  • 权限最小化:给服务主体或托管标识只分配 Get 和 List 就够了,不要给 Set、Delete 除非真有必要。我见过因为给 CI/CD 管道配了全部权限,导致一次误操作删掉整个 Secret 的事故。
  • 托管标识的局限:它只有在你部署在 Azure 内部资源上才能用,本地开发环境就不要想了。这时候可以用 AzureCliCredential 或 EnvironmentCredential 做 fallback。

一句话总结:能用托管标识就别用服务主体,能用服务主体就别用用户账号。身份认证越「免密」,后续的运维烦恼越少。