如何调用云端Dynamics 365并模拟当前登录Windows用户?
我有一个自定义ASP.NET Core 8.0 Web应用,希望从中调用Dynamics 365并模拟当前登录的Windows用户。该代码调用Dynamics v9.1本地部署版本时正常运行,但调用云端Dynamics 365时返回“unauthorized”错误。我猜测云端版Dynamics的机制有所不同,但无法确定修复方法,希望得到建议。
目前我在本地开发环境运行代码,模拟自身用户,且在本地和云端均为系统管理员。
代码如下(Get()方法为起始点,已在应用中提前调用):
protected virtual async Task<string> Get() { var handler = new HttpClientHandler { UseDefaultCredentials = true }; // The guid is a system administrator return await WindowsIdentity.RunImpersonated(_user.AccessToken, () => GetAsync("https://myorganizationname.crm4.dynamics.com/api/data/v9.2/", "systemusers(E8F2BDBD-540C-EF11-9F89-000D3A4C32E3)", handler)); } private async Task<string> GetAsync(string baseUrl, string url, HttpClientHandler handler) { using (var client = new HttpClient(handler)) { client.BaseAddress = new Uri(baseUrl); client.DefaultRequestHeaders.Add("Accept", "application/json"); try { var request = new HttpRequestMessage(HttpMethod.Get, url); var response = await client.SendAsync(request); var json = await response.Content.ReadAsStringAsync(); if (!response.IsSuccessStatusCode) { if (response.GetType() == typeof(HttpResponseMessage)) { throw new Exception(response.ReasonPhrase); // I always get "Unauthorized" as ReasonPhrase... } throw new Exception(json); } return json; } catch (System.Net.WebException ex) { // Just some error handling... } } }
如上所述,该代码调用本地Dynamics实例时运行正常。我该如何修改才能成功调用云端实例?
解决方案
核心原因
本地Dynamics部署支持NTLM/Windows身份验证,但云端Dynamics 365仅支持OAuth 2.0协议。你当前代码中UseDefaultCredentials = true依赖的NTLM身份验证在云端环境不被认可,这是导致Unauthorized错误的直接原因。
修改步骤
切换到OAuth 2.0身份验证
使用Azure AD的OAuth流程获取访问令牌,推荐使用Microsoft.Identity.Client(MSAL)库实现。需要先在Azure AD中注册应用,并配置对Dynamics 365的user_impersonation委托权限(需管理员同意)。调整用户模拟逻辑
WindowsIdentity.RunImpersonated无法适配云端OAuth场景,需通过当前登录用户的Azure AD身份获取针对Dynamics 365的访问令牌,以此实现模拟访问。
修改后的示例代码
using Microsoft.Identity.Client; // 建议从appsettings.json读取配置参数 private readonly string _clientId = "你的Azure AD应用客户端ID"; private readonly string _tenantId = "你的租户ID"; private readonly string _dynamicsResourceUrl = "https://myorganizationname.crm4.dynamics.com"; private readonly IHttpContextAccessor _httpContextAccessor; // 构造函数注入IHttpContextAccessor public YourService(IHttpContextAccessor httpContextAccessor) { _httpContextAccessor = httpContextAccessor; } protected virtual async Task<string> Get() { var accessToken = await GetDynamicsAccessTokenAsync(); return await GetAsync($"{_dynamicsResourceUrl}/api/data/v9.2/", "systemusers(E8F2BDBD-540C-EF11-9F89-000D3A4C32E3)", accessToken); } private async Task<string> GetDynamicsAccessTokenAsync() { var authority = $"https://login.microsoftonline.com/{_tenantId}"; var app = PublicClientApplicationBuilder.Create(_clientId) .WithAuthority(authority) .Build(); // 获取当前用户的Azure AD对象ID var userObjectId = _httpContextAccessor.HttpContext.User.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier")?.Value; if (string.IsNullOrEmpty(userObjectId)) { throw new InvalidOperationException("无法获取当前用户身份标识"); } // 静默获取令牌(优先使用缓存) var accounts = await app.GetAccountsAsync(); var targetAccount = accounts.FirstOrDefault(a => a.HomeAccountId.ObjectId == userObjectId); var result = await app.AcquireTokenSilent(new[] { $"{_dynamicsResourceUrl}/.default" }, targetAccount) .ExecuteAsync(); return result.AccessToken; } private async Task<string> GetAsync(string baseUrl, string url, string accessToken) { using (var client = new HttpClient()) { client.BaseAddress = new Uri(baseUrl); client.DefaultRequestHeaders.Add("Accept", "application/json"); client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken); try { var request = new HttpRequestMessage(HttpMethod.Get, url); var response = await client.SendAsync(request); var json = await response.Content.ReadAsStringAsync(); if (!response.IsSuccessStatusCode) { throw new Exception($"请求失败: {response.ReasonPhrase} - {json}"); } return json; } catch (Exception ex) { // 自定义错误处理逻辑 throw; } } }
额外配置注意事项
- 在
Program.cs中添加builder.Services.AddHttpContextAccessor();,确保能获取当前用户上下文。 - Azure AD应用需配置正确的重定向URI(Web应用通常为
https://localhost:xxxx/signin-oidc),并授予Dynamics CRM的user_impersonation委托权限,完成后需管理员同意该权限。 - 本地开发时,确保Web应用启用Windows身份验证,且当前登录用户在Azure AD和Dynamics 365中均拥有管理员权限。
内容的提问来源于stack exchange,提问作者Toby Fieldgroove

