如何通过odata冒充用户

l--*_*''' 19 c# dynamics-crm azure-service-fabric dynamics-crm-2016 dynamics-crm-webapi

我们在2016年使用odata v8.1端点成功模仿用户.

请注意,预期的请求流程是:邮递员 - > LocalHost微服务 - > CRM

来自Postman的工作请求示例- > CRM (直接,无需通过微服务)

Accept:application/json
Content-Type:application/json; charset=utf-8
OData-MaxVersion:4.0
OData-Version:4.0
MSCRMCallerID:d994d6ff-5531-e711-9422-00155dc0d345
Cache-Control:no-cache
Run Code Online (Sandbox Code Playgroud)

反对odata终点: ..../api/data/v8.1/leads

请注意,仅当通过邮递员直接针对odata v8.1端点发出此选项时才会成功.

当试图做同样的事情,让本地运行的服务(邮差 - > LocalHost服务 - > CRM),这失败了,只是忽略??? MSCRMCallerID标头.

在检查从Postman传递给LocalHost微服务的头时,请求,由VS 2017中的调试器检查:

{Method: POST, RequestUri: 'https://.../api/data/v8.1/leads', Version: 1.1, Content: System.Net.Http.StringContent, Headers:
{
  OData-Version: 4.0
  OData-MaxVersion: 4.0
  MSCRMCallerID: D994D6FF-5531-E711-9422-00155DC0D345
  Cache-Control: no-cache
  Accept: application/json
  Content-Type: application/json; charset=utf-8
}}
Run Code Online (Sandbox Code Playgroud)

记录是成功创建的,但是在CreatedBy字段上是服务用户名而不是MSCRMCallerID用户名(d994d6ff-5531-e711-9422-00155dc0d345),并且CreatedOnBehalf字段为空.

我们做错了什么?

我们如何通过我们的服务来模拟这种模仿?

编辑+更多信息

请注意,我相信我已经包含了所有相关信息,但如果我没有,请告诉我在此问题上应该提供的其他输入.

我试过了什么?

  1. 改变了标题的顺序
  2. 玩标题的情况下
  3. 确保guid对用户进行冒充是正确的
  4. 确保用户同时具有委托和系统管理员角色(尽管这是无关紧要的,因为这在直接针对crm odata端点执行请求时起作用,而不是我们的服务公开的端点
  5. 试图对https和http执行请求
  6. 提琴手痕迹如下图所示

请注意,此fiddler跟踪是显示Postman - > Microservice请求的跟踪.它没有显示从localhost微服务到CRM的通信.(我不知道为什么,也许是因为它是加密的)

POST https://localhost:19081/.....Leads/API/leads HTTP/1.1
Host: localhost:19081
Connection: keep-alive
Content-Length: 84
Cache-Control: no-cache
Origin: chrome-extension://aicmkgpgakddgnaphhhpliifpcfhicfo
MSCRMCallerID: D994D6FF-5531-E711-9422-00155DC0D345
X-Postman-Interceptor-Id: d79b1d2e-2155-f2ec-4ad7-e9b63e7fb90d
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36
Content-Type: application/json; charset=UTF-8
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.8
Cookie: ai_user=Ka2Xn|2017-05-25T17:30:57.941Z

{
    "subject": "created by mscrmcaller user2: d994d6ff-5531-e711-9422-00155dc0d345"
}
Run Code Online (Sandbox Code Playgroud)

@Ram建议我们使用组织服务进行身份验证,考虑到我们是针对Web API执行的,这是一个选项吗?请求的令牌是否仍然有效.(请注意,这可能是一个愚蠢的问题,原因是因为我不了解身份验证的工作原理).

以下是我们如何在每次通话时进行身份验证的代码段:

//check headers to see if we got a redirect to the new location
            var shouldAuthenticate = redirectUri.AbsoluteUri.Contains("adfs/ls");

            if (!shouldAuthenticate)
            {
                return;
            }

            var adfsServerName = redirectUri.Authority;
            var queryParams = HttpUtility.ParseQueryString(redirectUri.Query);

            ServicePointManager.ServerCertificateValidationCallback +=
                (sender, cert, chain, sslPolicyErrors) => true;

            WSTrustChannelFactory factory = null;
            try
            {
                // use a UserName Trust Binding for username authentication
                factory = new WSTrustChannelFactory(
                    new UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential),
                    $"https://{adfsServerName}/adfs/services/trust/13/usernamemixed")
                {
                    Credentials =
                    {
                        UserName =
                        {
                            UserName = $"{credential.Domain}\\{credential.UserName}",
                            Password = credential.Password
                        }
                    },
                    TrustVersion = TrustVersion.WSTrust13
                };

                var rst = new RequestSecurityToken
                {
                    RequestType = RequestTypes.Issue,
                    AppliesTo = new EndpointReference(_client.BaseAddress.AbsoluteUri),
                    TokenType = "urn:oasis:names:tc:SAML:1.0:assertion",
                    KeyType = KeyTypes.Bearer
                };

                var channel = factory.CreateChannel();
                channel.Issue(rst, out RequestSecurityTokenResponse rstr);

                var fedSerializer = new WSFederationSerializer();
                var rstrContent = fedSerializer.GetResponseAsString(rstr, new WSTrustSerializationContext());

                // construct a authentication form
                var crmauthenticaionPostDictionary = new Dictionary<string, string>
                {
                    {"wa", queryParams["wa"]},
                    {"wresult", rstrContent},
                    {"wctx", queryParams["wctx"]}
                };

                // post the authentication form to the website. 
                var crmAuthorizationPostResponse = _client.PostAsync(_client.BaseAddress.AbsoluteUri, new FormUrlEncodedContent(crmauthenticaionPostDictionary)).Result;

                var crmAuthorizationPostResponseString = crmAuthorizationPostResponse.Content.ReadAsStringAsync().Result;
                //we  should be authenticated here
                if (
                    !(
                        // we are correctly authorized if we got redirected to the correct address that we
                        // were trying to reach in the first place.
                        crmAuthorizationPostResponse.StatusCode == HttpStatusCode.Redirect
                        && crmAuthorizationPostResponse.Headers.Location == authenticationTestUri
                    )
                )
                {
                    throw new Exception("ADFS Authentication to CRM failed.");
                }
Run Code Online (Sandbox Code Playgroud)

Aru*_*oth 3

当您向 CRM 发出 Postman 请求时,它的直接调用和 CRM 会以预期的方式处理它。

但在 Postman -> Microservice -> CRM 中,微服务和 CRM 之间的标头丢失了。

在您的微服务中,您必须手动将标头转发到 CRM SDK 调用。

HttpWebRequest myHttpWebRequest1= (HttpWebRequest)WebRequest.Create(uri);
myHttpWebRequest1.Headers.Add("MSCRMCallerID", "D994D6FF-5531-E711-9422-00155DC0D345");
Run Code Online (Sandbox Code Playgroud)

或者HTTP 标头转发(抱歉,我找不到适用于 Azure / C# 的标头转发)

在此输入图像描述

更新:

假设您正在按照此MSDN 示例在 C# 微服务中执行 CRM Web API 调用。我已经包含了我们需要的标头 - MSCRMCallerID。看看是否对你有帮助。

public async Task BasicCreateAndUpdatesAsync()
{
   Console.WriteLine("--Section 1 started--");
   string queryOptions;  //select, expand and filter clauses
                         //First create a new contact instance,  then add additional property values and update 
                         // several properties.
                         //Local representation of CRM Contact instance
   contact1.Add("firstname", "Peter");
   contact1.Add("lastname", "Cambel");

   HttpRequestMessage createRequest1 =
       new HttpRequestMessage(HttpMethod.Post, getVersionedWebAPIPath() + "contacts");
   createRequest1.Content = new StringContent(contact1.ToString(),
       Encoding.UTF8, "application/json");

createRequest1.Headers.Add("MSCRMCallerID", "D994D6FF-5531-E711-9422-00155DC0D345");

   HttpResponseMessage createResponse1 =
       await httpClient.SendAsync(createRequest1);

   if (createResponse1.StatusCode == HttpStatusCode.NoContent)  //204
   {
    Console.WriteLine("Contact '{0} {1}' created.",
        contact1.GetValue("firstname"), contact1.GetValue("lastname"));
    contact1Uri = createResponse1.Headers.
        GetValues("OData-EntityId").FirstOrDefault();
    entityUris.Add(contact1Uri);
    Console.WriteLine("Contact URI: {0}", contact1Uri);
   }
   else
   {
    Console.WriteLine("Failed to create contact for reason: {0}",
        createResponse1.ReasonPhrase);
    throw new CrmHttpResponseException(createResponse1.Content);
   }
}
Run Code Online (Sandbox Code Playgroud)