pbootcms网站模板|日韩1区2区|织梦模板||网站源码|日韩1区2区|jquery建站特效-html5模板网

異步調用時 Azure KeyVault Active Directory AcquireTokenA

Azure KeyVault Active Directory AcquireTokenAsync timeout when called asynchronously(異步調用時 Azure KeyVault Active Directory AcquireTokenAsync 超時)
本文介紹了異步調用時 Azure KeyVault Active Directory AcquireTokenAsync 超時的處理方法,對大家解決問題具有一定的參考價值,需要的朋友們下面隨著小編來一起學習吧!

問題描述

限時送ChatGPT賬號..

我已按照 Microsoft

第二個令牌請求使用單線程:

  1. GetKeyAsync() 在 ThreadASP 上調用 GetAccessToken()(不在單獨的線程上.)
  2. GetKeyAsync() 返回一個Task.
  3. 我們調用 GetResult() 阻塞 ThreadASP,直到 GetKeyAsync() 完成.
  4. GetAccessToken() 必須等到 ThreadASP 空閑,ThreadASP 必須等到 GetKeyAsync() 完成,GetKeyAsync() 必須等到 GetAccessToken() 完成.哦哦.
  5. 死鎖.

為什么?誰知道?!?

GetKeyAsync() 中必須有一些依賴于訪問令牌緩存狀態的流控制.流控制決定是否在自己的線程上運行GetAccessToken(),以及在什么時候返回Task.

解決方案:一直異步

為避免死鎖,最佳做法是一直使用異步".當我們調用來自外部庫的異步方法時尤其如此,例如 GetKeyAsync().重要的是不要強制方法與 Wait()結果,或<代碼>GetResult().相反,請使用 asyncawait 因為 await 會暫停方法而不是阻塞整個線程.

異步控制器動作

公共類 HomeController : 控制器{公共異步任務<ActionResult>指數(){var provider = new EncryptionProvider();等待提供者.GetKeyBundle();var x = provider.MyKeyBundle;返回視圖();}}

異步公共方法

由于構造函數不能是異步的(因為異步方法必須返回一個Task),我們可以將異步的東西放到一個單獨的公共方法中.

公共類 EncryptionProvider{////認證屬性省略公共 KeyBundle MyKeyBundle;公共加密提供者(){}公共異步任務 GetKeyBundle(){var keyVaultClient = new KeyVaultClient(GetAccessToken);var keyBundleTask = 等待 keyVaultClient.GetKeyAsync(KeyVaultUrl, KeyVaultEncryptionKeyName);MyKeyBundle = keyBundleTask;}私有異步任務<字符串>獲取訪問令牌(字符串權限、字符串資源、字符串范圍){TokenCache.DefaultShared.Clear();//重現問題var authContext = new AuthenticationContext(authority, TokenCache.DefaultShared);var clientCredential = new ClientCredential(ClientIdWeb, ClientSecretWeb);var 結果 = 等待 authContext.AcquireTokenAsync(resource, clientCredential);var token = result.AccessToken;返回令牌;}}

謎團解開了.:) 這是最終參考我的理解.

控制臺應用

我最初的答案是這個控制臺應用程序.它作為最初的故障排除步驟.它沒有重現問題.

控制臺應用每五分鐘循環一次,反復請求新的訪問令牌.在每個循環中,它都會輸出當前時間、到期時間和檢索到的密鑰的名稱.

在我的機器上,控制臺應用程序運行了 1.5 小時,并在原始過期后成功檢索到密鑰.

使用系統;使用 System.Collections.Generic;使用 System.Threading.Tasks;使用 Microsoft.Azure.KeyVault;使用 Microsoft.IdentityModel.Clients.ActiveDirectory;命名空間 ConsoleApp{課堂節目{私有靜態異步任務 RunSample(){var keyVaultClient = new KeyVaultClient(GetAccessToken);//創建一個密鑰 :)var keyCreate = 等待 keyVaultClient.CreateKeyAsync(保險庫:_keyVaultUrl,密鑰名稱:_keyVaultEncryptionKeyName,密鑰類型:_keyType,關鍵屬性:新的關鍵屬性(){啟用 = 真,過期 = UnixEpoch.FromUnixTime(int.MaxValue),NotBefore = UnixEpoch.FromUnixTime(0),},技術標簽: 新字典<字符串、字符串>{{ "目的", "StackOverflow 演示" }});Console.WriteLine(string.Format(已創建 {0}",keyCreate.KeyIdentifier.Name));//取回密鑰var keyRetrieve = 等待 keyVaultClient.GetKeyAsync(_keyVaultUrl,_keyVaultEncryptionKeyName);Console.WriteLine(string.Format("檢索到 {0}",keyRetrieve.KeyIdentifier.Name));}私有靜態異步任務<字符串>獲取訪問令牌(字符串權限、字符串資源、字符串范圍){var clientCredential = 新的 ClientCredential(_keyVaultAuthClientId,_keyVaultAuthClientSecret);var context = new AuthenticationContext(權威,TokenCache.DefaultShared);var result = await context.AcquireTokenAsync(resource, clientCredential);_expiresOn = 結果.ExpiresOn.DateTime;Console.WriteLine(DateTime.UtcNow.ToShortTimeString());Console.WriteLine(_expiresOn.ToShortTimeString());返回結果.AccessToken;}私有靜態日期時間_expiresOn;私有靜態字符串_keyVaultAuthClientId = "xxxxx-xxx-xxxxx-xxx-xxxxx",_keyVaultAuthClientSecret = "xxxxx-xxx-xxxxx-xxx-xxxxx",_keyVaultEncryptionKeyName = "MYENCRYPTIONKEY",_keyVaultUrl = "https://xxxxx.vault.azure.net/",_keyType = "RSA";靜態無效主要(字符串 [] 參數){var keepGoing = true;同時(繼續){RunSample().GetAwaiter().GetResult();//休眠五分鐘System.Threading.Thread.Sleep(new TimeSpan(0, 5, 0));如果(日期時間.UtcNow > _expiresOn){Console.WriteLine("---過期---");Console.ReadLine();}}}}}

I have setup Azure Keyvault on my ASP.Net MVC web application by following the example in Microsoft's Hello Key Vault sample application.

Azure KeyVault (Active Directory) AuthenticationResult by default has a one hour expiry. So after one hour, you must get a new authentication token. KeyVault is working as expected for the first hour after getting my first AuthenticationResult token, but after the 1 hour expiry, it fails to get a new token.

Unfortunately it took a failure on my production environment for me to realize this, as I never tested past one hour in development.

Anyways, after over two days of trying to figure out what was wrong with my keyvault code, I came up with a solution that fixes all of my problems - remove the asynchronous code - but it feels very hacky. I want to find out why it was not working in the first place.

My code looks like this:

public AzureEncryptionProvider() //class constructor
{
   _keyVaultClient = new KeyVaultClient(GetAccessToken);
   _keyBundle = _keyVaultClient
     .GetKeyAsync(_keyVaultUrl, _keyVaultEncryptionKeyName)
     .GetAwaiter().GetResult();
}

private static readonly string _keyVaultAuthClientId = 
    ConfigurationManager.AppSettings["KeyVaultAuthClientId"];

private static readonly string _keyVaultAuthClientSecret =
    ConfigurationManager.AppSettings["KeyVaultAuthClientSecret"];

private static readonly string _keyVaultEncryptionKeyName =
    ConfigurationManager.AppSettings["KeyVaultEncryptionKeyName"];

private static readonly string _keyVaultUrl = 
    ConfigurationManager.AppSettings["KeyVaultUrl"];

private readonly KeyBundle _keyBundle;
private readonly KeyVaultClient _keyVaultClient;

private static async Task<string> GetAccessToken(
    string authority, string resource, string scope)
{
   var clientCredential = new ClientCredential(
       _keyVaultAuthClientId, 
       _keyVaultAuthClientSecret);
   var context = new AuthenticationContext(
       authority, 
       TokenCache.DefaultShared);
   var result = context.AcquireToken(resource, clientCredential);
   return result.AccessToken;
}

The GetAccessToken method signature has to be asynchronous to pass into the new KeyVaultClient constructor, so I left the signature as async, but I removed the await keyword.

With the await keyword in there (the way it should be, and is in the sample):

private static async Task<string> GetAccessToken(string authority, string resource, string scope)
{
   var clientCredential = new ClientCredential(_keyVaultAuthClientId, _keyVaultAuthClientSecret);
   var context = new AuthenticationContext(authority, null);
   var result = await context.AcquireTokenAsync(resource, clientCredential);
   return result.AccessToken;
}

The program works fine the first time I run it. And for an hour, AcquireTokenAsync returns the same original authentication token which is great. But once the token expires, AcquiteTokenAsync should get a new token with a new expiry date. And it doesn't - the application just hangs. No error returned, nothing at all.

So calling AcquireToken instead of AcquireTokenAsync solves the problem, but I have no idea why. You'll also notice that I'm passing 'null' instead of 'TokenCache.DefaultShared' into the AuthenticationContext constructor in my sample code with async. This is to force the toke to expire immediately instead of after one hour. Otherwise, you have to wait an hour to reproduce the behavior.

I was able to reproduce this again in a brand new MVC project, so I don't think it has anything to do with my specific project. Any insight would be appreciated. But for now, I'm just not using async.

解決方案

Problem: deadlock

Your EncryptionProvider() is calling GetAwaiter().GetResult(). This blocks the thread, and on subsequent token requests, causes a deadlock. The following code is the same as yours is but separates things to facilitate explanation.

public AzureEncryptionProvider() // runs in ThreadASP
{
    var client = new KeyVaultClient(GetAccessToken);

    var task = client.GetKeyAsync(KeyVaultUrl, KeyVaultEncryptionKeyName);

    var awaiter = task.GetAwaiter();

    // blocks ThreadASP until GetKeyAsync() completes
    var keyBundle = awaiter.GetResult();
}

In both token requests, the execution starts in the same way:

  • AzureEncryptionProvider() runs in what we'll call ThreadASP.
  • AzureEncryptionProvider() calls GetKeyAsync().

Then things differ. The first token request is multi-threaded:

  1. GetKeyAsync() returns a Task.
  2. We call GetResult() blocking ThreadASP until GetKeyAsync() completes.
  3. GetKeyAsync() calls GetAccessToken() on another thread.
  4. GetAccessToken() and GetKeyAsync() complete, freeing ThreadASP.
  5. Our web page returns to the user. Good.

The second token request uses a single thread:

  1. GetKeyAsync() calls GetAccessToken() on ThreadASP (not on a separate thread.)
  2. GetKeyAsync() returns a Task.
  3. We call GetResult() blocking ThreadASP until GetKeyAsync() completes.
  4. GetAccessToken() must wait until ThreadASP is free, ThreadASP must wait until GetKeyAsync() completes, GetKeyAsync() must wait until GetAccessToken() completes. Uh oh.
  5. Deadlock.

Why? Who knows?!?

There must be some flow control within GetKeyAsync() that relies on the state of our access token cache. The flow control decides whether to run GetAccessToken() on its own thread and at what point to return the Task.

Solution: async all the way down

To avoid a deadlock, it is a best practice "to use async all the way down." This is especially true when we are calling an async method, such as GetKeyAsync(), that is from an external library. It is important not force the method to by synchronous with Wait(), Result, or GetResult(). Instead, use async and await because await pauses the method instead of blocking the whole thread.

Async controller action

public class HomeController : Controller
{
    public async Task<ActionResult> Index()
    {
        var provider = new EncryptionProvider();
        await provider.GetKeyBundle();
        var x = provider.MyKeyBundle;
        return View();
    }
}

Async public method

Since a constructor cannot be async (because async methods must return a Task), we can put the async stuff into a separate public method.

public class EncryptionProvider
{
    //
    // authentication properties omitted

    public KeyBundle MyKeyBundle;

    public EncryptionProvider() { }

    public async Task GetKeyBundle()
    {
        var keyVaultClient = new KeyVaultClient(GetAccessToken);
        var keyBundleTask = await keyVaultClient
            .GetKeyAsync(KeyVaultUrl, KeyVaultEncryptionKeyName);
        MyKeyBundle = keyBundleTask;
    }

    private async Task<string> GetAccessToken(
        string authority, string resource, string scope)
    {
        TokenCache.DefaultShared.Clear(); // reproduce issue 
        var authContext = new AuthenticationContext(authority, TokenCache.DefaultShared);
        var clientCredential = new ClientCredential(ClientIdWeb, ClientSecretWeb);
        var result = await authContext.AcquireTokenAsync(resource, clientCredential);
        var token = result.AccessToken;
        return token;
    }
}

Mystery solved. :) Here is a final reference that helped my understanding.

Console App

My original answer had this console app. It worked as an initial troubleshooting step. It did not reproduce the problem.

The console app loops every five minutes, repeatedly asking for a new access token. At each loop, it outputs the current time, the expiry time, and the name of the retrieved key.

On my machine, the console app ran for 1.5 hours and successfully retrieved a key after expiration of the original.

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Azure.KeyVault;
using Microsoft.IdentityModel.Clients.ActiveDirectory;

namespace ConsoleApp
{
    class Program
    {
        private static async Task RunSample()
        {
            var keyVaultClient = new KeyVaultClient(GetAccessToken);

            // create a key :)
            var keyCreate = await keyVaultClient.CreateKeyAsync(
                vault: _keyVaultUrl,
                keyName: _keyVaultEncryptionKeyName,
                keyType: _keyType,
                keyAttributes: new KeyAttributes()
                {
                    Enabled = true,
                    Expires = UnixEpoch.FromUnixTime(int.MaxValue),
                    NotBefore = UnixEpoch.FromUnixTime(0),
                },
                tags: new Dictionary<string, string> {
                    { "purpose", "StackOverflow Demo" }
                });

            Console.WriteLine(string.Format(
                "Created {0} ",
                keyCreate.KeyIdentifier.Name));

            // retrieve the key
            var keyRetrieve = await keyVaultClient.GetKeyAsync(
                _keyVaultUrl,
                _keyVaultEncryptionKeyName);

            Console.WriteLine(string.Format(
                "Retrieved {0} ",
                keyRetrieve.KeyIdentifier.Name));
        }

        private static async Task<string> GetAccessToken(
            string authority, string resource, string scope)
        {
            var clientCredential = new ClientCredential(
                _keyVaultAuthClientId,
                _keyVaultAuthClientSecret);

            var context = new AuthenticationContext(
                authority,
                TokenCache.DefaultShared);

            var result = await context.AcquireTokenAsync(resource, clientCredential);

            _expiresOn = result.ExpiresOn.DateTime;

            Console.WriteLine(DateTime.UtcNow.ToShortTimeString());
            Console.WriteLine(_expiresOn.ToShortTimeString());

            return result.AccessToken;
        }

        private static DateTime _expiresOn;
        private static string
            _keyVaultAuthClientId = "xxxxx-xxx-xxxxx-xxx-xxxxx",
            _keyVaultAuthClientSecret = "xxxxx-xxx-xxxxx-xxx-xxxxx",
            _keyVaultEncryptionKeyName = "MYENCRYPTIONKEY",
            _keyVaultUrl = "https://xxxxx.vault.azure.net/",
            _keyType = "RSA";

        static void Main(string[] args)
        {
            var keepGoing = true;
            while (keepGoing)
            {
                RunSample().GetAwaiter().GetResult();
                // sleep for five minutes
                System.Threading.Thread.Sleep(new TimeSpan(0, 5, 0)); 
                if (DateTime.UtcNow > _expiresOn)
                {
                    Console.WriteLine("---Expired---");
                    Console.ReadLine();
                }
            }
        }
    }
}

這篇關于異步調用時 Azure KeyVault Active Directory AcquireTokenAsync 超時的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!

【網站聲明】本站部分內容來源于互聯網,旨在幫助大家更快的解決問題,如果有圖片或者內容侵犯了您的權益,請聯系我們刪除處理,感謝您的支持!

相關文檔推薦

ASP.NET Core authenticating with Azure Active Directory and persisting custom Claims across requests(ASP.NET Core 使用 Azure Active Directory 進行身份驗證并跨請求保留自定義聲明)
ASP.NET Core 2.0 Web API Azure Ad v2 Token Authorization not working(ASP.NET Core 2.0 Web API Azure Ad v2 令牌授權不起作用)
ASP Core Azure Active Directory Login use roles(ASP Core Azure Active Directory 登錄使用角色)
How do I get Azure AD OAuth2 Access Token and Refresh token for Daemon or Server to C# ASP.NET Web API(如何獲取守護進程或服務器到 C# ASP.NET Web API 的 Azure AD OAuth2 訪問令牌和刷新令牌) - IT屋-程序員軟件開發技
.Net Core 2.0 - Get AAD access token to use with Microsoft Graph(.Net Core 2.0 - 獲取 AAD 訪問令牌以與 Microsoft Graph 一起使用)
Getting access token using email address and app password from oauth2/token(使用電子郵件地址和應用程序密碼從 oauth2/token 獲取訪問令牌)
主站蜘蛛池模板: 意大利Frascold/富士豪压缩机_富士豪半封闭压缩机_富士豪活塞压缩机_富士豪螺杆压缩机 | 意大利Frascold/富士豪压缩机_富士豪半封闭压缩机_富士豪活塞压缩机_富士豪螺杆压缩机 | 酒精检测棒,数显温湿度计,酒安酒精测试仪,酒精检测仪,呼气式酒精检测仪-郑州欧诺仪器有限公司 | 针焰试验仪,灼热丝试验仪,漏电起痕试验仪,水平垂直燃烧试验仪 - 苏州亚诺天下仪器有限公司 | 苏州注册公司_苏州代理记账_苏州工商注册_苏州代办公司-恒佳财税 | 青岛代理记账_青岛李沧代理记账公司_青岛崂山代理记账一个月多少钱_青岛德辉财税事务所官网 | 帽子厂家_帽子工厂_帽子定做_义乌帽厂_帽厂_制帽厂 | 微信小程序定制,广州app公众号商城网站开发公司-广东锋火 | 手板-手板模型-手板厂-手板加工-生产厂家,[东莞创域模型] | 碎石机设备-欧版反击破-欧版颚式破碎机(站)厂家_山东奥凯诺机械 高低温试验箱-模拟高低温试验箱订制-北京普桑达仪器科技有限公司【官网】 | 合肥白癜风医院_[治疗白癜风]哪家好_合肥北大白癜风医院 | 印刷人才网 印刷、包装、造纸,中国80%的印刷企业人才招聘选印刷人才网! | 探鸣起名网-品牌起名-英文商标起名-公司命名-企业取名包满意 | 茅茅虫AI论文写作助手-免费AIGC论文查重_写毕业论文降重 | VI设计-LOGO设计公司-品牌设计公司-包装设计公司-导视设计-杭州易象设计 | 洛阳装修公司-洛阳整装一站式品牌-福尚云宅装饰 | 氟塑料磁力泵-不锈钢离心泵-耐腐蚀化工泵厂家「皖金泵阀」 | 深圳南财多媒体有限公司介绍| 污水处理设备维修_污水处理工程改造_机械格栅_过滤设备_气浮设备_刮吸泥机_污泥浓缩罐_污水处理设备_污水处理工程-北京龙泉新禹科技有限公司 | 软文推广发布平台_新闻稿件自助发布_媒体邀约-澜媒宝 | 间甲酚,间甲酚厂家-山东祥东新材料 | 半自动预灌装机,卡式瓶灌装机,注射器灌装机,给药器灌装机,大输液灌装机,西林瓶灌装机-长沙一星制药机械有限公司 | 全自动定氮仪-半自动凯氏定氮仪厂家-祎鸿仪器 | 变压器配件,变压器吸湿器,武强县吉口变压器配件有限公司 | Type-c防水母座|贴片母座|耳机接口|Type-c插座-深圳市步步精科技有限公司 | 杭州标识标牌|文化墙|展厅|导视|户内外广告|发光字|灯箱|铭阳制作公司 - 杭州标识标牌|文化墙|展厅|导视|户内外广告|发光字|灯箱|铭阳制作公司 | 罗茨真空机组,立式无油往复真空泵,2BV水环真空泵-力侨真空科技 | 哲力实业_专注汽车涂料汽车漆研发生产_汽车漆|修补油漆品牌厂家 长沙一级消防工程公司_智能化弱电_机电安装_亮化工程专业施工承包_湖南公共安全工程有限公司 | 智能气瓶柜(大型气瓶储存柜)百科 | Akribis直线电机_直线模组_力矩电机_直线电机平台|雅科贝思Akribis-杭州摩森机电科技有限公司 | 超声波清洗机_大型超声波清洗机_工业超声波清洗设备-洁盟清洗设备 | 济南品牌包装设计公司_济南VI标志设计公司_山东锐尚文化传播 | ISO9001认证咨询_iso9001企业认证代理机构_14001|18001|16949|50430认证-艾世欧认证网 | 扬尘在线监测系统_工地噪声扬尘检测仪_扬尘监测系统_贝塔射线扬尘监测设备「风途物联网科技」 | 涡街流量计_LUGB智能管道式高温防爆蒸汽温压补偿计量表-江苏凯铭仪表有限公司 | 济南品牌包装设计公司_济南VI标志设计公司_山东锐尚文化传播 | 艾默生变频器,艾默生ct,变频器,ct驱动器,广州艾默生变频器,供水专用变频器,风机变频器,电梯变频器,艾默生变频器代理-广州市盟雄贸易有限公司官方网站-艾默生变频器应用解决方案服务商 | 台式核磁共振仪,玻璃软化点测定仪,旋转高温粘度计,测温锥和测温块-上海麟文仪器 | 网带通过式抛丸机,,网带式打砂机,吊钩式,抛丸机,中山抛丸机生产厂家,江门抛丸机,佛山吊钩式,东莞抛丸机,中山市泰达自动化设备有限公司 | 中视电广_短视频拍摄_短视频推广_短视频代运营_宣传片拍摄_影视广告制作_中视电广 | 四合院设计_四合院装修_四合院会所设计-四合院古建设计与建造中心1 |