【Azure Developer】解決Azure Key Vault管理Storage的示例代碼在中國區Azure遇見的各種認證/授權問題 - C# Example Code


問題描述

使用Azure密鑰保管庫(Key Vault)來托管存儲賬號(Storage Account)密鑰的示例中,從Github中下載的示例代碼在中國區Azure運行時候會遇見各種認證和授權問題,以下列舉出運行代碼中遇見的各種異常:

  1. "AADSTS90002: Tenant 'xxxxxxxx-66d7-xxxx-8f9f-xxxxxxxxxxxx' not found. This may happen if there are no active subscriptions for the tenant. Check to make sure you have the correct tenant ID. Check with your subscription administrator.
  2. Microsoft.Rest.Azure.CloudException |  HResult=0x80131500 |  Message=The subscription 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' could not be found. |  Source=Microsoft.Azure.Management.KeyVault
  3. The client 'xxxxxxxx-e256-xxxx-8ef8-xxxxxxxxxxxx' with object id 'xxxxxxxx-e256-xxxx-xxxxxxxxxxxx' does not have authorization to perform action 'Microsoft.KeyVault/vaults/read' over scope '/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/dev-service-rg/providers/Microsoft.KeyVault/vaults/<youkeyvaultname>' or the scope is invalid. If access was recently granted, please refresh your credentials.

  4. Unexpected exception encountered: AADSTS700016: Application with identifier '54d5b1e9-5f5c-48f1-8483-d72471cbe7e7' was not found in the directory 'xxxxxxxx-66d7-xxxx-8f9f-xxxxxxxxxxxx'. This can happen if the application has not been installed by the administrator of the tenant or consented to by any user in the tenant. You may have sent your authentication request to the wrong tenant.

  5. {"AADSTS7000218: The request body must contain the following parameter: 'client_assertion' or 'client_secret'.\r\nTrace ID: 57169df7-d54d-4533-b6cf-fc269ee93f00\r\nCorrelation ID: 33fb61c4-7266-4690-bb8d-4d4ebb5614f5\r\nTimestamp: 2021-01-19 02:44:50Z"}
  6. AADSTS54005: OAuth2 Authorization code was already redeemed, please retry with a new valid code or use an existing refresh token. |Trace ID: cbfb3d00-a3e5-445e-96b3-918a94054100 |Correlation ID: 40964a5f-e267-43da-988a-00bf33fa7ad4 |Timestamp: 2021-01-19 03:16:38Z

 

以上錯誤就是在調試Key vault dotnet managed storage代碼的過程(https://github.com/Azure-Samples/key-vault-dotnet-managed-storage)中遇見的錯誤。下面我們一一的解決以上錯誤並使得程序成功運行:

 

調試代碼

首先通過Github下載代碼並在Azure環境中准備好AAD,Key Vault,Storage Account。

 PS: 獲取AAD中注冊應用的相應配置值,可以參考博文

【Azure Developer】使用Postman獲取Azure AD中注冊應用程序的授權Token,及為Azure REST API設置Authorization 

【Azure Developer】Python代碼通過AAD認證訪問微軟Azure密鑰保管庫(Azure Key Vault)中機密信息(Secret)

 

第一個錯誤:"AADSTS90002: Tenant 'xxxxxxxx-66d7-xxxx-8f9f-xxxxxxxxxxxx' not found. This may happen if there are no active subscriptions for the tenant. Check to make sure you have the correct tenant ID. Check with your subscription administrator.

這是因為代碼默認是連接到Global Azure的AAD環境,所以認證的時候會把app.config中的tenant值在Global Azure AAD中查找。而我們在項目中配置的Tenant是中國區Azure的,所以出現not found的提示。 只需要在代碼中指定AAD的環境中Azure China即可解決該問題。

  • ClientContext.cs文件中 修改GetServiceCredentialsAsync方法ActiveDirectoryServiceSettings.AzureActiveDirectoryServiceSettings.AzureChina
        /// <summary>
        /// Returns a task representing the attempt to log in to Azure public as the specified
        /// service principal, with the specified credential.
        /// </summary>
        /// <param name="certificateThumbprint"></param>
        /// <returns></returns>
        public static Task<ServiceClientCredentials> GetServiceCredentialsAsync(string tenantId, string applicationId, string appSecret)
        {
            if (_servicePrincipalCredential == null)
            {
                _servicePrincipalCredential = new ClientCredential(applicationId, appSecret);
            }

            //Update the Azure to Azure China
            return ApplicationTokenProvider.LoginSilentAsync(
                tenantId,
                _servicePrincipalCredential,
                ActiveDirectoryServiceSettings.AzureChina,
                TokenCache.DefaultShared);
        }

 

第二個錯誤:Microsoft.Rest.Azure.CloudException |  HResult=0x80131500 |  Message=The subscription 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' could not be found. |  Source=Microsoft.Azure.Management.KeyVault

這個錯誤的原因為在Azure Management KeyVault對象中找不到我們在項目中配置的訂閱信息。在Debug代碼時候才發現,KeyVaultManagementClient對象默認的URL也是指向Global Azure。中國區的Key Vault Management的URL為https://management.chinacloudapi.cn, 與Global不同。需要在KeyVaultSampleBase.cs代碼中設定ManagementClient.BaseUri = new Uri("https://management.chinacloudapi.cn"); 即可。

        private void InstantiateSample(string tenantId, string appId, string appSecret, string subscriptionId, string resourceGroupName, string vaultLocation, string vaultName, string storageAccountName, string storageAccountResourceId)
        {
            context = ClientContext.Build(tenantId, appId, appSecret, subscriptionId, resourceGroupName, vaultLocation, vaultName, storageAccountName, storageAccountResourceId);

            // log in with as the specified service principal for vault management operations
            var serviceCredentials = Task.Run(() => ClientContext.GetServiceCredentialsAsync(tenantId, appId, appSecret)).ConfigureAwait(true).GetAwaiter().GetResult();
// instantiate the management client
            ManagementClient = new KeyVaultManagementClient(serviceCredentials);
            ManagementClient.BaseUri = new Uri("https://management.chinacloudapi.cn");
            ManagementClient.SubscriptionId = subscriptionId;

            // instantiate the data client, specifying the user-based access token retrieval callback
            DataClient = new KeyVaultClient(ClientContext.AcquireUserAccessTokenAsync);
        }

 

第三個錯誤:The client 'xxxxxxxx-e256-xxxx-8ef8-xxxxxxxxxxxx' with object id 'xxxxxxxx-e256-xxxx-xxxxxxxxxxxx' does not have authorization to perform action 'Microsoft.KeyVault/vaults/read' over scope '/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/dev-service-rg/providers/Microsoft.KeyVault/vaults/<youkeyvaultname>' or the scope is invalid. If access was recently granted, please refresh your credentials.

 

 

在通過代碼獲取Key Vault Management對象時候,由於程序當前使用的AAD注冊應用沒有被授予Key Vault的操作權限,所以出現does not have authorization to perform action 'Microsoft.KeyVault/vaults/read' 。通過到Key Vault的門戶中為AAD應用分配權限即可解決此問題。

  • Key Vault Portal -> Access Control(IAM) -> Add Role Assignment.

 

 

第四個錯誤:Unexpected exception encountered: AADSTS700016: Application with identifier '54d5b1e9-5f5c-48f1-8483-d72471cbe7e7' was not found in the directory 'xxxxxxxx-66d7-xxxx-8f9f-xxxxxxxxxxxx'. This can happen if the application has not been installed by the administrator of the tenant or consented to by any user in the tenant. You may have sent your authentication request to the wrong tenant.

這個錯誤很迷惑,因為identifier “54d5b1e9-5f5c-48f1-8483-d72471cbe7e7”並不包含在配置中,它是如何產生的呢? 在全局查看項目文件后,發現它是代碼中hardcode的一個值。需要在使用時候把替換為app.config中的application id。

  • SampleConstants.cs文件中的WellKnownClientId
        public static string WellKnownClientId
        {
            // Native AD app id with permissions in the subscription
            // Consider fetching it from configuration.
            get
            {
                return "54d5b1e9-5f5c-48f1-8483-d72471cbe7e7";
            }
        }
  • ClientContext.cs文件中使用ConfigurationManager.AppSettings[SampleConstants.ConfigKeys.VaultMgmtAppId]替換WellKnownClientId
        public static async Task<string> AcquireUserAccessTokenAsync(string authority, string resource, string scope)
        {
            var context = new AuthenticationContext(authority, TokenCache.DefaultShared);
            if (_deviceCodeResponse == null)
            {
                //_deviceCodeResponse = await context.AcquireDeviceCodeAsync(resource, SampleConstants.WellKnownClientId).ConfigureAwait(false);
                _deviceCodeResponse = await context.AcquireDeviceCodeAsync(resource, ConfigurationManager.AppSettings[SampleConstants.ConfigKeys.VaultMgmtAppId]).ConfigureAwait(false);

                Console.WriteLine("############################################################################################");
                Console.WriteLine("To continue with the test run, please follow these instructions: {0}", _deviceCodeResponse.Message);
                Console.WriteLine("############################################################################################");
            }

            //context.AcquireTokenAsync()

            var result = await context.AcquireTokenByDeviceCodeAsync(_deviceCodeResponse).ConfigureAwait(false);
            return result.AccessToken;
        }

 

第五個錯誤:{"AADSTS7000218: The request body must contain the following parameter: 'client_assertion' or 'client_secret'.\r\nTrace ID: 57169df7-d54d-4533-b6cf-fc269ee93f00\r\nCorrelation ID: 33fb61c4-7266-4690-bb8d-4d4ebb5614f5\r\nTimestamp: 2021-01-19 02:44:50Z"}

這個錯誤是在 context.AcquireTokenByDeviceCodeAsync時,由於配置的AAD應用中沒有開啟移動應用或客戶端應用的高級設置。詳細的分析可以參考博客:https://blogs.aaddevsup.xyz/2019/08/receiving-error-aadsts7000218-the-request-body-must-contain-the-following-parameter-client_assertion-or-client_secret/

 

 

第六個錯誤:AADSTS54005: OAuth2 Authorization code was already redeemed, please retry with a new valid code or use an existing refresh token. |Trace ID: cbfb3d00-a3e5-445e-96b3-918a94054100 |Correlation ID: 40964a5f-e267-43da-988a-00bf33fa7ad4 |Timestamp: 2021-01-19 03:16:38Z

這個錯誤發生在 retrievedMsaResponse = await sample.DataClient.GetStorageAccountWithHttpMessagesAsync(vaultUri, managedStorageName).ConfigureAwait(false)的部分,由於通過AcquireTokenByDeviceCodeAsync獲取到的token只能完成一次認證。所以再一次調用KeyVaultClient的Get-xxxxx-WithHttpMessageAsync時就會出現OAuth2 Authorization code was already redeemed錯誤。 解決方法為修改創建 DataClient = new KeyVaultClient(ClientContext.AcquireUserAccessTokenAsync)的認證方式或者是AcquireUserAccessTokenAsync中的獲取token方式。

private void InstantiateSample(string tenantId, string appId, string appSecret, string subscriptionId, string resourceGroupName, string vaultLocation, string vaultName, string storageAccountName, string storageAccountResourceId)
        {
            context = ClientContext.Build(tenantId, appId, appSecret, subscriptionId, resourceGroupName, vaultLocation, vaultName, storageAccountName, storageAccountResourceId);

            // log in with as the specified service principal for vault management operations
            var serviceCredentials = Task.Run(() => ClientContext.GetServiceCredentialsAsync(tenantId, appId, appSecret)).ConfigureAwait(true).GetAwaiter().GetResult();
            //var serviceCredentials =ClientContext.GetServiceCredentialsAsync(tenantId, appId, appSecret).Result;

            // instantiate the management client
            ManagementClient = new KeyVaultManagementClient(serviceCredentials);
            ManagementClient.BaseUri = new Uri("https://management.chinacloudapi.cn");
            ManagementClient.SubscriptionId = subscriptionId;

            // instantiate the data client, specifying the user-based access token retrieval callback
            DataClient = new KeyVaultClient(ClientContext.AcquireUserAccessTokenAsync);
       //DataClient = new KeyVaultClient(serviceCredentials); }

或者

        public static async Task<string> AcquireUserAccessTokenAsync(string authority, string resource, string scope)
        {
            var context = new AuthenticationContext(authority, TokenCache.DefaultShared);
            if (_deviceCodeResponse == null)
            {
                //_deviceCodeResponse = await context.AcquireDeviceCodeAsync(resource, SampleConstants.WellKnownClientId).ConfigureAwait(false);
                _deviceCodeResponse = await context.AcquireDeviceCodeAsync(resource, ConfigurationManager.AppSettings[SampleConstants.ConfigKeys.VaultMgmtAppId]).ConfigureAwait(false);

                Console.WriteLine("############################################################################################");
                Console.WriteLine("To continue with the test run, please follow these instructions: {0}", _deviceCodeResponse.Message);
                Console.WriteLine("############################################################################################");
            }

            //context.AcquireTokenAsync()

            var result = await context.AcquireTokenByDeviceCodeAsync(_deviceCodeResponse).ConfigureAwait(false);
       return result.AccessToken; }

(PS: 以上第六個錯誤還沒有完全解決。)

 

參考資料:

創建 SAS 定義,並通過編寫代碼提取共享訪問簽名令牌:https://docs.azure.cn/zh-cn/key-vault/secrets/storage-keys-sas-tokens-code

Azure Sample:https://github.com/Azure-Samples

RECEIVING ERROR AADSTS7000218: THE REQUEST BODY MUST CONTAIN THE FOLLOWING PARAMETER: ‘CLIENT_ASSERTION’ OR ‘CLIENT_SECRET’ :https://blogs.aaddevsup.xyz/2019/08/receiving-error-aadsts7000218-the-request-body-must-contain-the-following-parameter-client_assertion-or-client_secret/

Python代碼通過AAD認證訪問微軟Azure密鑰保管庫(Azure Key Vault)中機密信息(Secret)https://www.cnblogs.com/lulight/p/14286396.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM