KeyVault GetSecretAsync 永远不会返回

问题描述 投票:0回答:5

在 Web 应用程序中使用 KeyVault 的示例代码包含以下代码:

public static async Task<string> GetSecret(string secretId)
{
    var secret = await keyVaultClient.GetSecretAsync(secretId);
    return secret.Value;
}

我已将示例中包含的

KeyVaultAccessor
对象合并到我的应用程序中以对其进行测试。该调用作为对我的 Web API 控制器方法之一的查询的一部分来执行:

var secret = KeyVaultAccessor.GetSecret("https://superSecretUri").Result;

不幸的是,调用永远不会返回并且查询无限期挂起......

可能是什么原因,因为坦白说我不知道从哪里开始......?

c# .net azure async-await
5个回答
15
投票

这是我在我的博客上完整描述的常见死锁问题。简而言之,async

 方法在 
await
 完成后尝试返回 ASP.NET 请求上下文,但该请求一次只允许一个线程,并且该上下文中已经存在一个线程(被阻止的线程)致电 
Result
)。因此,任务正在等待上下文空闲,而线程正在阻塞上下文,直到任务完成:死锁。

正确的解决方案是使用

await

 而不是 
Result
:

var secret = await KeyVaultAccessor.GetSecret("https://superSecretUri");
    

15
投票
我使用以下代码来覆盖同步上下文:

var secret = Task.Run(async () => await KeyVaultAccessor.GetSecretAsync("https://superSecretUri")).Result;

如果您使用非异步方法,这仍然可以让您使用

.Result


    


5
投票
不幸的是,调用永远不会返回并且查询无限期挂起......

你遇到了典型的僵局。这就是为什么

你不应该阻塞异步代码。在幕后,编译器生成一个状态机并捕获称为 SynchronizationContext

 的东西。当您同步阻止调用线程时,尝试将延续发布回同一上下文会导致死锁。 

不要使用

.Result

 同步阻塞,而是让控制器异步并等待从 
Task
 返回的 
GetSecret

public async IHttpActionResult FooAsync() { var secret = await KeyVaultAccessor.GetSecretAsync("https://superSecretUri"); return Ok(); }

旁注 - 异步方法应遵循命名约定并以 Async

 为后缀。


0
投票
GetSecretAsync 返回一个 Response 对象。

Response<KeyVaultSecret> Response = await SecretClient.GetSecretAsync("SecretName");
调用Response.Value,不返回秘密值;相反,它返回一个 KeyVaultSecret 对象。

KeyVaultSecret Secret = Response.Value;
检索秘密值:

string SecretValue = Secret.Value;
我写这个答案是因为我一直将 Response.Value 传递给 API,认为我正在传递秘密值。难怪我收到了 403 Forbidden 错误。


-2
投票
使用其余 API...

public class AzureKeyVaultClient { public string GetSecret(string name, string vault) { var client = new RestClient($"https://{vault}.vault.azure.net/"); client.Authenticator = new AzureAuthenticator($"https://vault.azure.net"); var request = new RestRequest($"secrets/{name}?api-version=2016-10-01"); request.Method = Method.GET; var result = client.Execute(request); if (result.StatusCode != HttpStatusCode.OK) { Trace.TraceInformation($"Unable to retrieve {name} from {vault} with response {result.Content}"); var exception = GetKeyVaultErrorFromResponse(result.Content); throw exception; } else { return GetValueFromResponse(result.Content); } } public string GetValueFromResponse(string content) { var result = content.FromJson<keyvaultresponse>(); return result.value; } public Exception GetKeyVaultErrorFromResponse(string content) { try { var result = content.FromJson<keyvautlerrorresponse>(); var exception = new Exception($"{result.error.code} {result.error.message}"); if(result.error.innererror!=null) { var innerException = new Exception($"{result.error.innererror.code} {result.error.innererror.message}"); } return exception; } catch(Exception e) { return e; } } class keyvaultresponse { public string value { get; set; } public string contentType { get; set; } } class keyvautlerrorresponse { public keyvaulterror error {get;set;} } class keyvaulterror { public string code { get; set; } public string message { get; set; } public keyvaulterror innererror { get; set; } } class AzureAuthenticator : IAuthenticator { private string _authority; private string _clientId; private string _clientSecret; private string _resource; public AzureAuthenticator(string resource) { _authority = WebConfigurationManager.AppSettings["azure:Authority"]; _clientId = WebConfigurationManager.AppSettings["azure:ClientId"]; _clientSecret = WebConfigurationManager.AppSettings["azure:ClientSecret"]; _resource = resource; } public AzureAuthenticator(string resource, string tennant, string clientid, string secret) { //https://login.microsoftonline.com/<tennant>/oauth2/oken _authority = authority; //azure client id (web app or native app _clientId = clientid; //azure client secret _clientSecret = secret; //vault.azure.net _resource = resource; } public void Authenticate(IRestClient client, IRestRequest request) { var token = GetS2SAccessTokenForProdMSA().AccessToken; //Trace.WriteLine(String.Format("obtained bearer token {0} from ADAL and adding to rest request",token)); request.AddHeader("Authorization", String.Format("Bearer {0}", token)); } public AuthenticationResult GetS2SAccessTokenForProdMSA() { return GetS2SAccessToken(_authority, _resource, _clientId, _clientSecret); } private AuthenticationResult GetS2SAccessToken(string authority, string resource, string clientId, string clientSecret) { var clientCredential = new ClientCredential(clientId, clientSecret); AuthenticationContext context = new AuthenticationContext(authority, false); AuthenticationResult authenticationResult = context.AcquireToken( resource, clientCredential); return authenticationResult; } } }
    
© www.soinside.com 2019 - 2024. All rights reserved.