如何让Azure Function代表已认证调用方访问Azure资源?
实现Azure Function代表已认证调用方访问Azure资源
要让Azure Function承接已登录AD用户的身份,去访问Cosmos DB、Key Vault等资源,核心是用On-Behalf-Of (OBO) 令牌委派流程,结合Azure AD认证和RBAC权限控制,步骤如下:
1. 配置Azure Function的AD身份验证
- 给你的Function App启用Azure AD身份验证:在Azure门户的Function App「身份验证」面板,选择「添加身份提供者」,选Azure AD,配置为「需要身份验证」(拒绝未认证的请求)。
- 关联的应用注册需支持获取用户的
access_token(而非仅id_token),若为前端调用Function,还需开启隐式流中的access_token选项。
2. 配置应用注册的委派权限
- 找到Function对应的Azure AD应用注册,在「API权限」中添加以下委派权限(按需调整):
- Cosmos DB:
Microsoft.DocumentDB/databaseAccounts/readMetadata、Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/*(或更细粒度的数据权限) - Key Vault:
Microsoft.KeyVault/vaults/secrets/read、Microsoft.KeyVault/vaults/secrets/write等 - 若要检查用户组成员身份,需添加Microsoft Graph的
GroupMember.Read.All委派权限
- Cosmos DB:
- 点击「授予管理员同意」,确保权限生效(避免用户每次调用都需授权)。
3. 用OBO流程获取资源访问令牌
在Function代码中,通过用户的身份令牌,请求目标资源的访问令牌,以代表用户访问资源:
代码示例(C#)
using Microsoft.Identity.Client; using System.Net.Http.Headers; using Microsoft.Azure.Cosmos; public static async Task<IActionResult> Run(HttpRequest req, ILogger log) { // 从请求头提取用户的Bearer令牌 if (!req.Headers.TryGetValue("Authorization", out var authHeader) || !authHeader.ToString().StartsWith("Bearer ")) { return new UnauthorizedResult(); } var userToken = authHeader.ToString().Substring("Bearer ".Length); // 初始化MSAL客户端 var clientId = Environment.GetEnvironmentVariable("AzureAD_ClientId"); var clientSecret = Environment.GetEnvironmentVariable("AzureAD_ClientSecret"); var authority = Environment.GetEnvironmentVariable("AzureAD_Authority"); // 格式:https://login.microsoftonline.com/{租户ID} var confidentialClient = ConfidentialClientApplicationBuilder .Create(clientId) .WithClientSecret(clientSecret) .WithAuthority(new Uri(authority)) .Build(); // 请求Cosmos DB的访问令牌 var cosmosScope = "https://cosmos.azure.com/.default"; var userAssertion = new UserAssertion(userToken); var tokenResult = await confidentialClient.AcquireTokenOnBehalfOf(new[] { cosmosScope }, userAssertion).ExecuteAsync(); // 使用令牌访问Cosmos DB var cosmosClient = new CosmosClient("https://your-cosmos-account.documents.azure.com:443/", tokenResult.AccessToken); var database = cosmosClient.GetDatabase("your-db"); var container = database.GetContainer("your-container"); // 执行Cosmos DB操作(如查询、写入)... // 同理可获取Key Vault令牌,请求scope为"https://vault.azure.net/.default" return new OkObjectResult("资源访问完成"); }
4. 配置资源的RBAC权限
- 针对Cosmos DB、Key Vault,将用户所属的AD组添加到对应的RBAC角色中:
- Cosmos DB:可分配
Cosmos DB Built-in Data Reader/Cosmos DB Built-in Data Contributor等数据角色 - Key Vault:可分配
Key Vault Secrets Reader/Key Vault Secrets Contributor等角色
- Cosmos DB:可分配
- 当用用户委派的令牌访问资源时,Azure会自动验证用户(或其所属组)的权限,返回对应访问结果。
5. 可选:在Function中提前验证用户组
如果需要在Function层面先判断用户是否属于指定组,可调用Microsoft Graph API获取用户的组成员身份:
// 获取Graph API的令牌(OBO流程,scope为"https://graph.microsoft.com/.default") var graphTokenResult = await confidentialClient.AcquireTokenOnBehalfOf(new[] { "https://graph.microsoft.com/.default" }, userAssertion).ExecuteAsync(); // 调用Graph API获取用户所属组 var graphClient = new HttpClient(); graphClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", graphTokenResult.AccessToken); var groupsResponse = await graphClient.GetAsync($"https://graph.microsoft.com/v1.0/me/memberOf"); if (!groupsResponse.IsSuccessStatusCode) { return new ForbidResult(); } // 解析响应,检查是否包含允许的组ID/名称,不符合则返回403 var groups = await groupsResponse.Content.ReadFromJsonAsync<dynamic>(); var allowedGroupId = "your-group-id"; var isInAllowedGroup = groups.value.Any(g => g.id == allowedGroupId); if (!isInAllowedGroup) { return new ForbidResult(); }
关键注意事项
- OBO流程要求Function的应用注册是机密客户端,需配置客户端密钥或证书,不能用公共客户端。
- 缓存获取到的资源令牌,避免频繁调用MSAL,提升性能。
- 处理令牌过期、权限不足等异常,返回合适的HTTP状态码(401 Unauthorized、403 Forbidden)。
内容的提问来源于stack exchange,提问作者webish
相关产品推荐
相关产品推荐

