You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何调用云端Dynamics 365并模拟当前登录Windows用户?

问题:ASP.NET Core 8调用云端Dynamics 365返回Unauthorized错误,本地部署正常

我有一个自定义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错误的直接原因。

修改步骤

  1. 切换到OAuth 2.0身份验证
    使用Azure AD的OAuth流程获取访问令牌,推荐使用Microsoft.Identity.Client(MSAL)库实现。需要先在Azure AD中注册应用,并配置对Dynamics 365的user_impersonation委托权限(需管理员同意)。

  2. 调整用户模拟逻辑
    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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 21:04:55