【Azure Developer】解决Azure Key Vault管理Storage的示例代码在中国区Azure遇见的各种认证/授权问题 – C# Example Code,【Azure Developer】使用Postman获取Azure AD中注册应用程序的授权Token,及为Azure REST API设置Authorization,【Azure Developer】Python代码通过AAD认证访问微软Azure密钥保管库(Azure Key Vault)中机密信息(Secret)

Jenkins自动部署spring boot

问题描述

使用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);
        }

 

【老孟Flutter】源码分析系列之InheritedWidget

第三个错误: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

Windows DHCP最佳实践(四)

给TA买糖
共{{data.count}}人
人已赞赏
经验教程

Flink源码剖析:Jar包任务提交流程

2021-1-19 21:19:00

经验教程

Jenkins自动部署spring boot

2021-1-19 21:29:00

⚠️
免责声明:根据《计算机软件保护条例》第十七条规定“为了学习和研究软件内含的设计思想和原理,通过安装、显示、传输或者存储软件等方式使用软件的,可以不经软件著作权人许可,不向其支付报酬。”您需知晓本站所有内容资源均来源于网络,仅供用户交流学习与研究使用,版权归属原版权方所有,版权争议与本站无关,用户本人下载后不能用作商业或非法用途,需在24个小时之内从您的电脑中彻底删除上述内容,否则后果均由用户承担责任;如果您访问和下载此文件,表示您同意只将此文件用于参考、学习而非其他用途,否则一切后果请您自行承担,如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。 本站为个人博客非盈利性站点,所有软件信息均来自网络,所有资源仅供学习参考研究目的,并不贩卖软件,不存在任何商业目的及用途,网站会员捐赠是您喜欢本站而产生的赞助支持行为,仅为维持服务器的开支与维护,全凭自愿无任何强求。本站部份代码及教程来源于互联网,仅供网友学习交流,若您喜欢本文可附上原文链接随意转载。
无意侵害您的权益,请发送邮件至 momeis6@qq.com 或点击右侧 私信:momeis 反馈,我们将尽快处理。
0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
今日签到
有新私信 私信列表
搜索