【问题标题】:.Net Core 2.0 - Get AAD access token to use with Microsoft Graph.Net Core 2.0 - 获取 AAD 访问令牌以与 Microsoft Graph 一起使用
【发布时间】:2018-03-15 22:57:45
【问题描述】:

使用 Azure AD 身份验证启动新的 .Net Core 2.0 项目时,您会获得一个可以登录租户的工作示例,太棒了!

现在我想获取已登录用户的访问令牌,并使用它与 Microsoft Graph API 一起工作。

我没有找到任何关于如何实现这一点的文档。我只想要一种简单的方法来获取访问令牌并访问图形 API,使用启动新 .NET Core 2.0 项目时创建的模板。从那里我应该能够弄清楚其余的。

在 Visual Studio 中创建新的 2.0 MVC Core 应用程序时,它适用于在执行选择工作和学校帐户进行身份验证的过程时创建的项目。

【问题讨论】:

    标签: asp.net azure asp.net-core azure-active-directory asp.net-core-2.0


    【解决方案1】:

    我写了一篇博客文章来说明如何做到这一点:ASP.NET Core 2.0 Azure AD Authentication

    TL;DR 是当您从 AAD 收到授权码时,您应该添加这样的处理程序:

    .AddOpenIdConnect(opts =>
    {
        Configuration.GetSection("Authentication").Bind(opts);
    
        opts.Events = new OpenIdConnectEvents
        {
            OnAuthorizationCodeReceived = async ctx =>
            {
                var request = ctx.HttpContext.Request;
                var currentUri = UriHelper.BuildAbsolute(request.Scheme, request.Host, request.PathBase, request.Path);
                var credential = new ClientCredential(ctx.Options.ClientId, ctx.Options.ClientSecret);
    
                var distributedCache = ctx.HttpContext.RequestServices.GetRequiredService<IDistributedCache>();
                string userId = ctx.Principal.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
    
                var cache = new AdalDistributedTokenCache(distributedCache, userId);
    
                var authContext = new AuthenticationContext(ctx.Options.Authority, cache);
    
                var result = await authContext.AcquireTokenByAuthorizationCodeAsync(
                    ctx.ProtocolMessage.Code, new Uri(currentUri), credential, ctx.Options.Resource);
    
                ctx.HandleCodeRedemption(result.AccessToken, result.IdToken);
            }
        };
    });
    

    这里我的context.Options.Resourcehttps://graph.microsoft.com (Microsoft Graph),我从配置中绑定它以及其他设置(客户端 ID 等)。

    我们使用 ADAL 兑换令牌,并将生成的令牌存储在令牌缓存中。

    令牌缓存是你必须做的,这里是来自example app的例子:

    public class AdalDistributedTokenCache : TokenCache
    {
        private readonly IDistributedCache _cache;
        private readonly string _userId;
    
        public AdalDistributedTokenCache(IDistributedCache cache, string userId)
        {
            _cache = cache;
            _userId = userId;
            BeforeAccess = BeforeAccessNotification;
            AfterAccess = AfterAccessNotification;
        }
    
        private string GetCacheKey()
        {
            return $"{_userId}_TokenCache";
        }
    
        private void BeforeAccessNotification(TokenCacheNotificationArgs args)
        {
            Deserialize(_cache.Get(GetCacheKey()));
        }
    
        private void AfterAccessNotification(TokenCacheNotificationArgs args)
        {
            if (HasStateChanged)
            {
                _cache.Set(GetCacheKey(), Serialize(), new DistributedCacheEntryOptions
                {
                    AbsoluteExpirationRelativeToNow = TimeSpan.FromDays(1)
                });
                HasStateChanged = false;
            }
        }
    }
    

    这里的令牌缓存使用分布式缓存来存储令牌,以便为您的应用提供服务的所有实例都可以访问令牌。它们按用户缓存,因此您可以稍后为任何用户检索令牌。

    然后,当您想要获取令牌并使用 MS 图时,您会执行类似(GetAccessTokenAsync() 中的重要内容):

    [Authorize]
    public class HomeController : Controller
    {
        private static readonly HttpClient Client = new HttpClient();
        private readonly IDistributedCache _cache;
        private readonly IConfiguration _config;
    
        public HomeController(IDistributedCache cache, IConfiguration config)
        {
            _cache = cache;
            _config = config;
        }
    
        [AllowAnonymous]
        public IActionResult Index()
        {
            return View();
        }
    
        public async Task<IActionResult> MsGraph()
        {
            HttpResponseMessage res = await QueryGraphAsync("/me");
    
            ViewBag.GraphResponse = await res.Content.ReadAsStringAsync();
    
            return View();
        }
    
        private async Task<HttpResponseMessage> QueryGraphAsync(string relativeUrl)
        {
            var req = new HttpRequestMessage(HttpMethod.Get, "https://graph.microsoft.com/v1.0" + relativeUrl);
    
            string accessToken = await GetAccessTokenAsync();
            req.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
    
            return await Client.SendAsync(req);
        }
    
        private async Task<string> GetAccessTokenAsync()
        {
            string authority = _config["Authentication:Authority"];
    
            string userId = User.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
            var cache = new AdalDistributedTokenCache(_cache, userId);
    
            var authContext = new AuthenticationContext(authority, cache);
    
            string clientId = _config["Authentication:ClientId"];
            string clientSecret = _config["Authentication:ClientSecret"];
            var credential = new ClientCredential(clientId, clientSecret);
    
            var result = await authContext.AcquireTokenSilentAsync("https://graph.microsoft.com", credential, new UserIdentifier(userId, UserIdentifierType.UniqueId));
    
            return result.AccessToken;
        }
    }
    

    我们在此处静默获取令牌(使用令牌缓存),并将其附加到对 Graph 的请求中。

    【讨论】:

    • 我看到你甚至在你的帖子末尾提到它 :) 在创建新的 2.0 MVC Core 应用程序时,你没有碰巧使用工作和学校帐户进行身份验证的示例?我已经尝试将您的示例集成到其中一个项目中,但没有成功。明天再试试。
    • 我认为在该模板中您必须修改添加身份验证的扩展方法。你应该可以很容易地应用它。
    • 明天我会尽快尝试并通知您!
    • 令人惊讶的是我让它工作了。阅读您的博客有助于理解流程,谢谢!我有一个问题。感觉就像 .AddAzureAd 部分和您在 VS 示例中获得的相关扩展方法做了很多您在示例中所做的事情,所以我可能会做一些事情两次。还是我错了?因为如果我注释掉 .AddAzureAd 部分,它仍然只适用于您的 .AddOpenIdConnect 方法。
    • 它做同样的事情。它只是将身份验证处理程序配置封装在扩展方法中,因此 Startup 中的代码更少。所以你可以像我一样做,也可以修改扩展方法来做同样的事情。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-31
    相关资源
    最近更新 更多