我最近做了类似的事情,但没有依赖 Azure 订阅身份验证功能来连接回 D365。在我的情况下,呼叫来自其他地方的 Azure 函数,但返回的连接没有什么不同。在任何这些情况下,身份验证都不会通过。如果 AAD 用户向您的 Function 应用程序进行身份验证,您仍需要使用应用程序用户连接到 D365,然后模拟呼叫您的用户。
首先,确保您在 Azure AD 中的 App Registrations 下注册的应用程序属于“Web 应用程序/API”类型,而不是“Native”类型。编辑已注册应用的设置并确保以下内容:
- 不考虑应用程序 ID,我稍后将其称为 appId。
- 在“API 访问 - 所需权限”下,添加 Dynamics CRM Online (Microsoft.CRM) 而不是 Dynamics 365。
- 在“API 访问 - 密钥”下,创建一个具有适当有效期的密钥。如果您有多个功能/应用程序作为此“应用程序”连接回来,您可以创建多个键。我稍后会将此密钥称为“clientSecret”。
如果“Keys”选项不可用,则您已注册了本机应用程序。
我将 appId 和 clientSecret 存储在 Function App 的应用程序配置部分,并使用通常的 System.Configuration.ConfigurationManager.AppSettings 集合访问它们。
以下示例使用对 AuthenticationParameters 的调用来查找权限和资源 URL,但您可以使用在线无数示例轻松手动构建这些 URL。我发现如果它们发生变化,它只会自行更新,所以以后的工作会更少。
这些都是简单的例子,我只是在掩饰刷新令牌和所有这些东西的需要。
然后使用 OData 访问 D365:
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
}
}
}
}
如果您想使用组织服务和 LINQ 访问 D365,请使用以下内容。我花了一段时间才弄清楚的两个主要部分是那个奇怪的 organization.svc URL 的格式,以及使用 Microsoft.Xrm.Sdk.WebServiceClient.OrganizationWebProxyClient 而不是 Tooling:
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"]);
}
}
不确定您在 Webhook 注册中返回什么上下文,尚未尝试过,但只需确保 Authorization 标头中有一个不记名令牌通常会这样做,并且上面的两个示例以不同的方式注入它,所以您应该能够将这里需要的东西拼接在一起。