如何在Azure函数应用中使用服务主体调用方身份访问受保护API
在Azure函数中以已认证服务主体调用方身份访问ARM的解决方案
方案一:调用方传递ARM访问令牌给函数应用
这是最直接的实现方式,利用服务主体本身可获取ARM令牌的特性,将令牌直接传递给函数应用使用:
- 调用方(服务主体)通过客户端凭据流获取ARM的访问令牌:
// 调用方Node.js代码示例 const { ClientCredentialCredential } = require("@azure/identity"); const credential = new ClientCredentialCredential("<租户ID>", "<服务主体Client ID>", "<服务主体密钥>"); const armToken = await credential.getToken("https://management.azure.com/.default"); - 调用函数时,将令牌放入请求的
Authorization头,格式为Bearer <arm-token> - 函数应用从请求头提取令牌,直接用于ARM API调用:
// Azure函数Node.js代码示例 module.exports = async function (context, req) { const armToken = req.headers.authorization?.split(' ')[1]; if (!armToken) { context.res = { status: 400, body: "缺少ARM访问令牌" }; return; } const armResponse = await fetch( "https://management.azure.com/subscriptions/{订阅ID}/resourcegroups?api-version=2021-04-01", { headers: { 'Authorization': `Bearer ${armToken}` } } ); const armData = await armResponse.json(); context.res = { body: armData }; }; - 核心前提:调用方服务主体需对目标ARM资源拥有对应RBAC权限(如
Reader、Contributor)
方案二:函数应用通过托管标识获取调用方服务主体的ARM令牌
若无法让调用方传递令牌,可通过函数的用户分配托管标识,结合AAD权限配置,为调用方服务主体获取ARM令牌:
- 权限配置:给函数的用户分配托管标识授予AAD应用权限:
Application.Read.All(读取服务主体信息)和DelegatedPermissionGrant.ReadWrite.All(管理委托权限),并完成管理员同意 - 获取调用方标识:函数从EasyAuth注入的
x-ms-client-principal-id头中提取调用方服务主体的appId - 请求ARM令牌并调用API:
// Azure函数Node.js代码示例 const { DefaultAzureCredential } = require("@azure/identity"); const credential = new DefaultAzureCredential({ managedIdentityClientId: "<用户分配托管标识的Client ID>" }); module.exports = async function (context, req) { const callerAppId = req.headers['x-ms-client-principal-id']; if (!callerAppId) { context.res = { status: 400, body: "无法获取调用方标识" }; return; } const tenantId = "<租户ID>"; // 获取Graph API令牌用于调用AAD服务 const graphToken = await credential.getToken("https://graph.microsoft.com/.default"); // 请求调用方服务主体的ARM访问令牌 const tokenRes = await fetch(`https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ client_id: "<函数托管标识的Client ID>", grant_type: "on_behalf_of", assertion: graphToken.token, requested_token_use: "on_behalf_of", scope: "https://management.azure.com/.default", subject_token: callerAppId, subject_token_type: "urn:ietf:params:oauth:token-type:jwt" }) }); const tokenData = await tokenRes.json(); const armToken = tokenData.access_token; // 调用ARM API const armResponse = await fetch( "https://management.azure.com/subscriptions/{订阅ID}/resourcegroups?api-version=2021-04-01", { headers: { 'Authorization': `Bearer ${armToken}` } } ); const armData = await armResponse.json(); context.res = { body: armData }; };
- 注意:此方案依赖AAD OBO流对服务主体的兼容支持,需测试验证,同时要严格控制托管标识的权限范围,避免过度授权
关键注意事项
- 两种方案都需确保调用方服务主体对目标ARM资源拥有足够的RBAC权限
- 方案一无需函数额外配置权限,更轻量化;方案二适合无法修改调用方逻辑的场景
内容的提问来源于stack exchange,提问作者Jurjen
相关产品推荐
相关产品推荐

