C# 从Azure函数向Dynamics 365进行身份验证 脚本

C# 从Azure函数向Dynamics 365进行身份验证 脚本,c#,azure,dynamics-crm,azure-functions,dynamics-365,C#,Azure,Dynamics Crm,Azure Functions,Dynamics 365,我有一个在线托管的Dynamics 365 v9组织。我在我的Dynamics组织的另一个租户上的Azure Function应用程序中托管了一组Azure功能 我是使用Dynamics插件注册工具创建的,在某些事件中(例如在Dynamics中创建联系人时),该工具会通过其端点URL将数据发布到我的Azure函数 Dynamics 365和my Azure函数之间的身份验证是通过在HTTP请求的身份验证HttpHeader中传递x-Functions-key值来实现的 Azure函数从Dynam

我有一个在线托管的Dynamics 365 v9组织。我在我的Dynamics组织的另一个租户上的Azure Function应用程序中托管了一组Azure功能

我是使用Dynamics插件注册工具创建的,在某些事件中(例如在Dynamics中创建联系人时),该工具会通过其端点URL将数据发布到我的Azure函数

Dynamics 365和my Azure函数之间的身份验证是通过在HTTP请求的身份验证HttpHeader中传递
x-Functions-key
值来实现的

Azure函数从Dynamics中的事件接收数据,数据形式为a,我可以使用以下代码读取:

using System.Net;

public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
    var jsonContent = await req.Content.ReadAsStringAsync();

    log.Info(jsonContent);

    return req.CreateResponse(HttpStatusCode.OK);
}
使用System.Net;
公共静态异步任务运行(HttpRequestMessage请求、TraceWriter日志)
{
var jsonContent=await req.Content.ReadAsStringAsync();
log.Info(jsonContent);
返回请求CreateResponse(HttpStatusCode.OK);
}
问题 Azure功能如何通过调用Dynamics 365组织的身份验证来读取和写入数据

我试过的
  • Xrm工具
  • 最简单的身份验证方法是使用from Microsoft.Xrm.Tooling.Connector.dll。但是,我不需要用户名和密码来提供CrmServiceClient的构造函数。也许可以通过HTTP POST请求安全地传递凭据

  • 应用程序用户
  • 我已尝试在Dynamics中注册应用程序用户。我为我的Azure功能提供了客户端id和客户端机密,但身份验证失败,因为用户与我的Azure功能位于不同的租户中

    深思熟虑的解决方案 收到的
    jsonContent
    字符串的一个对象称为
    ParentContext
    。也许这可以被重用以向调用Dynamics的组织进行身份验证


    Marc Schweigert建议使用S2S,并向其存储库提供了一个示例。如果我能让这种方法发挥作用,我会在这里发布解决方案。

    这也是我很好奇的事情,但我还没有机会对此进行实验

    对于第二种选择,您是否已在目标AAD中注册申请并授予同意

    当他们授予许可时,您注册的应用程序将添加到Azure AD企业应用程序列表中,Azure AD租户的用户可以使用该应用程序

    只有在管理员授予许可后,您才能在订阅服务器的Dynamics 365租户中创建应用程序用户

    我认为访问问题的根源与应用程序的服务主体对象(目标租户的本地对象)有关

    服务主体对象

    为了访问由Azure AD租户保护的资源,需要访问的实体必须由安全主体表示。用户(用户主体)和应用程序(服务主体)都是如此。安全主体定义该租户中用户/应用程序的访问策略和权限。这将启用核心功能,例如登录期间的用户/应用程序身份验证,以及资源访问期间的授权

    将应用程序对象视为应用程序的全局表示形式,以便在所有租户中使用,将服务主体视为本地表示形式,以便在特定租户中使用


    -克里斯

    这也是我很好奇的事情,但我还没有机会对此进行实验

    对于第二种选择,您是否已在目标AAD中注册申请并授予同意

    当他们授予许可时,您注册的应用程序将添加到Azure AD企业应用程序列表中,Azure AD租户的用户可以使用该应用程序

    只有在管理员授予许可后,您才能在订阅服务器的Dynamics 365租户中创建应用程序用户

    我认为访问问题的根源与应用程序的服务主体对象(目标租户的本地对象)有关

    服务主体对象

    为了访问由Azure AD租户保护的资源,需要访问的实体必须由安全主体表示。用户(用户主体)和应用程序(服务主体)都是如此。安全主体定义该租户中用户/应用程序的访问策略和权限。这将启用核心功能,例如登录期间的用户/应用程序身份验证,以及资源访问期间的授权

    将应用程序对象视为应用程序的全局表示形式,以便在所有租户中使用,将服务主体视为本地表示形式,以便在特定租户中使用


    -克里斯

    我没想到你能明智地使用“真实”用户凭据连接到CRM

    我将使用服务帐户连接回CRM。创建新的CRM 用户,尤其是出于此目的,如果您使用户非交互式,则不应使用许可证。然后,您可以使用该服务帐户的凭据使用
    CrmServiceClient
    连接到CRM。或者看一看

    如果您能够向功能应用程序提供用户id,则可以通过CRM web服务向“真实”用户使用服务帐户

    要模拟用户,请在的实例上设置CallerId属性 OrganizationServiceProxy,然后调用服务的Web方法


    我没想到你能明智地使用“真实”用户凭据连接到CRM

    我将使用服务帐户连接回CRM。创建新的CRM 用户,尤其是出于此目的,如果您使用户非交互式,则不应使用许可证。然后可以使用c
    string odataUrl = "https://org.crm6.dynamics.com/api/data/v8.2/"; // trailing slash actually matters
    string appId = "some-guid";
    string clientSecret = "some key";
    
    AuthenticationParameters authArg = AuthenticationParameters.CreateFromResourceUrlAsync(new Uri(odataUrl)).Result;
    AuthenticationContext authCtx = new AuthenticationContext(authArg.Authority);
    AuthenticationResult authRes = authCtx.AcquireTokenAsync(authArg.Resource, new ClientCredential(appId, clientSecret)).Result;
    
    using (HttpClient client = new HttpClient()) {
      client.TimeOut = TimeSpan.FromMinutes (2);
      client.DefaultRequestHeaders.Add("Authorization", authRes.CreateAuthorizationHeader ());
      using (HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Get, $"{odataUrl}accounts?$select=name&$top=10")) {
        using (HttpResponseMessage res = client.SendAsync(req).Result) {
          if (res.IsSuccessStatusCode) {
            Console.WriteLine(res.Content.ReadAsStringAsync().Result);
          }
          else {
            // cry
          }
        }
      }
    }
    
    string odataUrl = "https://org.crm6.dynamics.com/xrmservices/2011/organization.svc/web?SdkClientVersion=8.2"; // don't question the url, just accept it.
    string appId = "some-guid";
    string clientSecret = "some key";
    
    AuthenticationParameters authArg = AuthenticationParameters.CreateFromResourceUrlAsync(new Uri(odataUrl)).Result;
    AuthenticationContext authCtx = new AuthenticationContext(authArg.Authority);
    AuthenticationResult authRes = authCtx.AcquireTokenAsync(authArg.Resource, new ClientCredential(appId, clientSecret)).Result;
    
    using (OrganizationWebProxyClient webProxyClient = new OrganizationWebProxyClient(new Uri(orgSvcUrl), false)) {
      webProxyClient.HeaderToken = authRes.AccessToken;
      using (OrganizationServiceContext ctx = new OrganizationServiceContext((IOrganizationService)webProxyClient)) {
        var accounts = (from i in ctx.CreateQuery("account") orderby i["name"] select i).Take(10);
        foreach (var account in accounts)
          Console.WriteLine(account["name"]);
      }
    }
    
      var clientcred = new ClientCredential(clientId, clientSecret);
                    AuthenticationContext authContext = new AuthenticationContext(aadInstance, false);
                    AuthenticationResult result = authContext.AcquireToken(organizationUrl, clientcred);
    
    
                    token = result.AccessToken;
                    ExpireDate = result.ExpiresOn.DateTime;
    
    
                    client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);