總結一下最近項目上使用azure 遇到的問題。
azure functions 使用 aad auoth 2.0 身份證書身份認證。從國外一個博客上學到的知識。信息量非常大,博客中介紹了如何使用key vault 創建證書。

先簡單介紹一下原理,然后再講一下容易出錯的細節
azure funcation: funcApi; 對應的 app叫 funApiApp ,配置一個客戶端app 叫 clientApp.
我們把前期工作先做好:
1 創建azure funcation funcAPI
2.在azure activity directory 中注冊兩個APP。funApiApp 和clientApp。
3.在key vault 中創建證書。並將CER格式的證書下載。
1 配置 azure function 使用 AAD身份認證,使用Express模式,選擇前期工作中創建的APP:funApiApp
2 配置APP:funApiApp ,編輯mainfest: 添加appRoles 將保存
"appRoles": [
{
"allowedMemberTypes": [
"Application"
],
"description": "Accesss the service-api",
"displayName": "service-api",
"id": "d5deb0a5-0d04-4740-a48d-d6b92c3578db",
"isEnabled": true,
"lang": null,
"origin": "Application",
"value": "service-api"
},
{
"allowedMemberTypes": [
"Application"
],
"description": "Accesss the access_as_applicaton",
"displayName": "access_as_application",
"id": "d3deb0a5-0d04-4740-a48d-d6b92c3578db",
"isEnabled": true,
"lang": null,
"origin": "Application",
"value": "access_as_application"
}
],
3 配置clientAPP,上傳在前期工作中3步的證書上傳

4 添加權限,添加對funcApiApp的權限


5。獲取funapiApp的 appid.及剛才的URL
6.獲取clientapp的clientid
7。配置key vault的訪問權限,可以在代碼獲取證書文件。
Get started with Key Vault certificates | Microsoft Docs
8 獲取證書,獲取token,調用API代碼如下
public static async Task Test()
{
// Use Key Vault to get certificate
var azureServiceTokenProvider = new AzureServiceTokenProvider();
// Get the certificate from Key Vault
var identifier = Environment.GetEnvironmentVariable("KeyVaultCertificateName");//Environment.GetEnvironmentVariable("ClientCertificates:0:KeyVaultCertificateName"];
var cert = await GetCertificateAsync(identifier);
var scope =Environment.GetEnvironmentVariable("ScopeForAccessToken");
var authority = $"{Environment.GetEnvironmentVariable("Instance")}{Environment.GetEnvironmentVariable("TenantId")}";
// client credentials flows, get access token
IConfidentialClientApplication app = ConfidentialClientApplicationBuilder
.Create(Environment.GetEnvironmentVariable("ClientId"))
.WithAuthority(new Uri(authority))
.WithCertificate(cert)
.WithLogging(MyLoggingMethod, Microsoft.Identity.Client.LogLevel.Verbose,
enablePiiLogging: true, enableDefaultPlatformLogging: true)
.Build();
var accessToken = await app.AcquireTokenForClient(new[] { scope }).ExecuteAsync();
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(Environment.GetEnvironmentVariable("ApiBaseAddress"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken.AccessToken);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// use access token and get payload
var content = new StringContent("", Encoding.UTF8, "application/json");
var response = await client.PostAsync(Environment.GetEnvironmentVariable("TargetURL"), content); ;
if (response.IsSuccessStatusCode)
{
var responseContent = await response.Content.ReadAsStringAsync();
var data = JArray.Parse(responseContent);
//eturn data;
}
}
private static async Task<X509Certificate2> GetCertificateAsync(string identitifier)
{
var vaultBaseUrl =Environment.GetEnvironmentVariable("KeyVaultUrl");
// including AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, and AZURE_TENANT_ID.
var secretClient = new SecretClient(vaultUri: new Uri(vaultBaseUrl),
credential: new DefaultAzureCredential());
// Create a new secret using the secret client.
var secretName = identitifier;
//var secretVersion = "";
KeyVaultSecret secret = await secretClient.GetSecretAsync(secretName);
var privateKeyBytes = Convert.FromBase64String(secret.Value);
var certificateWithPrivateKey = new X509Certificate2(privateKeyBytes,
(string)null,
X509KeyStorageFlags.MachineKeySet);
return certificateWithPrivateKey;
}
static void MyLoggingMethod(Microsoft.Identity.Client.LogLevel level, string message, bool containsPii)
{
_log.LogInformation($"MSAL {level} {containsPii} {message}");
}
PS:獲取key vault 的配置需要配置環境變量

在項目中設置環境變量, // Create a new certificate client using the default credential from Azure.Identity using environment variables previously set, // including AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, and AZURE_TENANT_ID. var client = new CertificateClient(vaultUri: new Uri(keyVaultUrl), credential: new DefaultAzureCredential());
其它配置文件:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"ScopeForAccessToken": "http://XXXXX/.default",
"ApiBaseAddress": "https://localhost:44390",
"Instance": "https://login.microsoftonline.com/",
"Domain": "damienbodhotmail.onmicrosoft.com",
"TenantId": "XXXXX,
"ClientId": "XXXXX",
"SourceType": "KeyVault",
"KeyVaultUrl": "https://XXX.vault.azure.net/",
"KeyVaultCertificateName": "XXXX"
}
}
原理圖

