调用Azure管理API获取函数密钥时遭遇403 Forbidden错误求助
调用以下HTTP POST请求获取Azure函数密钥时,收到403禁止访问错误:
URL:
https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Web/sites/{appName}/functions/{functionName}/listkeys?api-version=2022-03-01
错误信息:
StatusCode: 403, ReasonPhrase: 'Forbidden'
客户端代码
let tenantId = "<some_tenant_id>" let clientId = "<some_client_id>" let secret = "<some_secret>" let scope = "<some_scope>" let token = BearerToken.Create(tenantId, clientId, secret, scope).Result let tokenRequestBody = Dictionary<string, string>() tokenRequestBody.Add("grant_type" , "client_credentials") tokenRequestBody.Add("client_id" , clientId) tokenRequestBody.Add("client_secret", secret) tokenRequestBody.Add("scope" , scope) let content = new FormUrlEncodedContent(tokenRequestBody); let httpKeysClient = new HttpClient(); httpKeysClient.DefaultRequestHeaders.Authorization <- new AuthenticationHeaderValue("Bearer", token); httpKeysClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); let subscriptionId = "<some_scubscription_id>" let resourceGroupName = "<some_resource_group_name>" let appName = "<some_function_app_name>" let functionName = "<some_function_name>" let apiKeyUrl = $"https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Web/sites/{appName}/functions/{functionName}/listkeys?api-version=2022-03-01"; let response = httpKeysClient.PostAsync(apiKeyUrl, content).Result; response.IsSuccessStatusCode |> should equal true // ** StatusCode: 403, ReasonPhrase: 'Forbidden' **
令牌生成代码(可正常运行)
public static class BearerToken { public async static Task<string> Create(string tenantId, string clientId, string clientSecret, string scope) { var tokenRequestBody = new Dictionary<string, string> { { "grant_type" , "client_credentials" }, { "client_id" , clientId }, { "client_secret", clientSecret }, { "scope" , scope } }; var url = $"https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token"; var client = new HttpClient() { BaseAddress = new Uri(url) }; var content = new FormUrlEncodedContent(tokenRequestBody); var response = await client.PostAsync("", content); if (response.IsSuccessStatusCode) { var tokenResponse = await response.Content.ReadAsStringAsync(); var valueFor = JsonConvert.DeserializeObject<JsonSupport.AccessToken.Root>(tokenResponse); return valueFor.access_token; } throw new Exception(response.ReasonPhrase); } }
排查原因及解决方法
1. 请求体冗余导致格式错误
调用listkeys接口时不需要传递请求体,代码错误地将获取令牌时的Form请求体再次传递给了密钥接口,服务器收到不符合要求的请求内容后返回403错误。
解决:调用PostAsync时传入null作为内容,即执行httpKeysClient.PostAsync(apiKeyUrl, null)。
2. 服务主体权限不足
clientId对应的Azure AD服务主体没有足够权限访问函数密钥。
解决:在Azure门户中,找到目标函数应用所在的资源组或订阅,为该服务主体分配**网站参与者(Website Contributor)**角色,或者更细粒度的权限:Microsoft.Web/sites/functions/listkeys/action。
3. 令牌Scope设置错误
获取令牌时的scope必须是https://management.azure.com/.default,这是调用Azure管理API的固定scope。使用其他scope会导致令牌不具备访问管理API的权限,进而返回403。
解决:将代码中的scope值改为"https://management.azure.com/.default"。
4. 资源参数拼写错误
确认subscriptionId、resourceGroupName、appName、functionName的拼写完全正确,参数错误可能导致服务器无法找到资源,部分场景下会返回403错误。
内容的提问来源于stack exchange,提问作者Scott Nimrod

