Win32Exception:使用WebClient调用WCF服务时,目标主体名称不正确

时间:2014-11-06 20:30:33

标签: .net wcf kerberos

我将一大堆示例代码与linqpad放在一起,显示WCF webservice调用,而无需从WSDL创建代理类。这就是我所拥有的:

using (var wb = new WebClient())
{
  wb.Credentials = CredentialCache.DefaultNetworkCredentials;
  wb.Headers.Add("Content-Type: text/xml;charset=UTF-8");
  wb.Headers.Add("SOAPAction: \"http://tempuri.org/Core/Project_GetNumberForExternalUse\"");

  String requestString = @"
    <soapenv:Envelope xmlns:soapenv=""http://schemas.xmlsoap.org/soap/envelope/"" xmlns:tem=""http://tempuri.org/"">
    <soapenv:Header/>
    <soapenv:Body>
        <tem:Project_GetNumberForExternalUse>
          <!--Optional:-->
          <tem:extData1>ext 1</tem:extData1>
          <!--Optional:-->
          <tem:extData2>ext 2</tem:extData2>
          <!--Optional:-->
          <tem:extData3>ext 3</tem:extData3>
          <!--Optional:-->
          <tem:extData4>ext 4</tem:extData4>
          <!--Optional:-->
          <tem:extDataLong>ext 1ext 1ext 1ext 1ext 1</tem:extDataLong>
        </tem:Project_GetNumberForExternalUse>
    </soapenv:Body>
  </soapenv:Envelope>
  ";

  var response = wb.UploadString("http://myserver:55002/Core.svc", "POST", requestString);
  response.Dump();
}

这在连接到localhost但不是myserver时工作正常。 WCF服务配置为使用Windows身份验证。基于错误,我猜测问题是服务主体名称未包含在授权中。如果我使用带代理的WCF客户端,我将使用端点/身份配置部分解决此问题。但是,我没有看到在代码中设置此方法的方法。有没有办法这样做?还有其他方法可以解决这个问题吗?

2 个答案:

答案 0 :(得分:2)

答案 1 :(得分:1)

来自MSDN网站:

SPN是客户端唯一标识服务器上的服务或应用程序实例以进行相互身份验证的名称。默认情况下会请求相互身份验证,您可以通过在请求中将WebRequest.AuthenticationLevel设置为MutualAuthRequired来要求它。

当WebRequest需要相互身份验证时,目标的SPN必须由客户端提供。如果您知道SPN,则可以在发送请求之前将其添加到CustomTargetNameDictionary。如果您尚未向此字典添加SPN信息,则AuthenticationManager使用RequestUri方法编写最可能的SPN;但是,这是一个计算值,可能不正确。如果尝试进行相互身份验证并失败,则可以检查字典以确定计算的SPN。如果身份验证协议不支持相互身份验证,则不会在字典中输入SPN。

因此,添加到Authentication.CutomTargetNameDictionary网址和spn应解决您的问题。

using (var wb = new WebClient())
{
  const string URL = "http://myserver:55002/Core.svc";

  wb.Credentials = CredentialCache.DefaultNetworkCredentials;
  wb.Headers.Add("Content-Type: text/xml;charset=UTF-8");
  wb.Headers.Add("SOAPAction: \"http://tempuri.org/Core/Project_GetNumberForExternalUse\"");

  String requestString = @"
    <soapenv:Envelope xmlns:soapenv=""http://schemas.xmlsoap.org/soap/envelope/"" xmlns:tem=""http://tempuri.org/"">
    <soapenv:Header/>
    <soapenv:Body>
        <tem:Project_GetNumberForExternalUse>
          <!--Optional:-->
          <tem:extData1>ext 1</tem:extData1>
          <!--Optional:-->
          <tem:extData2>ext 2</tem:extData2>
          <!--Optional:-->
          <tem:extData3>ext 3</tem:extData3>
          <!--Optional:-->
          <tem:extData4>ext 4</tem:extData4>
          <!--Optional:-->
          <tem:extDataLong>ext 1ext 1ext 1ext 1ext 1</tem:extDataLong>
        </tem:Project_GetNumberForExternalUse>
    </soapenv:Body>
  </soapenv:Envelope>
  ";

  var uri = new Uri(URL);
  var spn = $"{uri.Scheme}/{uri.Authority}";
  AuthenticationManager.CustomTargetNameDictionary.Add(URL, spn);

  var response = wb.UploadString(URL, "POST", requestString);
  response.Dump();
}