HTTP proxy basic authentication with WCF ChannelBa

2019-06-08 14:54发布

问题:

I'm stuck with a problem of basic authentication to connect a web service which was created, in Visual Studio, deriving from ChannelBase. Already tried to ask in MSDN with no results, so I'm trying my luck over here.

This is the scenario:

a) the web service requires basic authentication (username and password) and supports SOAP 1.1

b) I have to go through a HTTP proxy which requires, again, basic authentication.

I cannot get this to work: the authentication on the web service is fine (if tested bypassing the proxy) but it looks like I cannot configure the ChannelBase-derived class to handle the authentication at the proxy (after getting an authentication required HTTP response it does not proceed negotiating the authentication).

here are my current settings in app.config

<configuration>
 <system.serviceModel>
  <bindings>
   <customBinding>
    <binding name="LocationWSBinding" receiveTimeout="00:02:30">
     <textMessageEncoding messageVersion="Soap11" />
     <httpTransport authenticationScheme="Basic" proxyAddress="http://The.proxy.ip" useDefaultWebProxy="false" 
      bypassProxyOnLocal="True" proxyAuthenticationScheme="Basic" />
    </binding>
   </customBinding>
  </bindings>
  <client>
   <endpoint address="http://the.web.service"
      binding ="customBinding" bindingConfiguration="LocationWSBinding"
     contract="The.Contract.Name" name="LocationWSPort" />
  </client>
 </system.serviceModel>
</configuration>

and in the code, this is the place where I am specifying the credentials:

TheClient WS = new TheClient("LocationWSPort");

  WS.ClientCredentials.UserName.UserName = "WebServiceUsername";
  WS.ClientCredentials.UserName.Password = "WebServicepassword";
  WS.ChannelFactory.Credentials.Windows.ClientCredential = new NetworkCredential("Proxyusername","ProxyPassword");

Can you please help me?

I don't know what's wrong which is blocking the client to proceed with proxy authentication when requested.

Thanks Emanuele


Enrico, sorry for the late answer. I have tried following your suggestion, and it works only if I call a webservice method without arguments, when I call a method requiring complex types as an argument (which is the real call I have to make to get useful payload) I get the following error:

{"The server did not provide a meaningful reply; this might be caused by a contract mismatch, a premature session shutdown or an internal server error."}
[System.ServiceModel.CommunicationException]: {"The server did not provide a meaningful reply; this might be caused by a contract mismatch, a premature session shutdown or an internal server error."}
_className: null
_data: {System.Collections.ListDictionaryInternal}
_dynamicMethods: null
_exceptionMethod: null
_exceptionMethodString: null
_helpURL: null
_HResult: -2146233087
_innerException: null
_ipForWatsonBuckets: 83189596
_message: "The server did not provide a meaningful reply; this might be caused by a contract mismatch, a premature session shutdown or an internal server error."
_remoteStackIndex: 1
_remoteStackTraceString: "\r\nServer stack trace: \r\n   at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)\r\n   at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)\r\n   at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)\r\n\r\nException rethrown at [0]: \r\n"
_safeSerializationManager: {System.Runtime.Serialization.SafeSerializationManager}
_source: null
_stackTrace: {sbyte[80]}
_stackTraceString: null
_watsonBuckets: null
_xcode: -532462766
_xptrs: 0
Data: {System.Collections.ListDictionaryInternal}
HelpLink: null
HResult: -2146233087
InnerException: null
IsTransient: false
Message: "The server did not provide a meaningful reply; this might be caused by a contract mismatch, a premature session shutdown or an internal server error."
Source: "mscorlib"
StackTrace: "\r\nServer stack trace: \r\n   at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)\r\n   at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)\r\n   at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)\r\n\r\nException rethrown at [0]: \r\n   at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)\r\n   at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)\r\n   at [.......] in E:\\VisualStudioPrj\\HTTPproxyTroubleshoot\\HTTPproxyTroubleshoot\\Program.cs:line 88"
TargetSite: {Void HandleReturnMessage(System.Runtime.Remoting.Messaging.IMessage, System.Runtime.Remoting.Messaging.IMessage)}

Now, I am sure that the data contract is OK (it is working if I do not set WebRequest.DefaultWebProxy). As for the FTP, well, these SOAP calls are done by one of the two threads run by the Windows service which coordinates them: the other is taking care of FTP calls instead, and I need these not to transit through the proxy otherwise the answer will be formatted as HTML which is messing around the FTP communications...

回答1:

The problem you're experiencing is due to the fact that WCF will use the same set of client credentials both for service authentication and for upstream web proxy authentication. Since your service requires a different pair of credentials than your upstream web proxy does, the request is not being properly authenticated and subsequently gets blocked.

Since you can specify only a single set of credentials on the ClientCredentials class, the solution is to set the credentials to use with the web proxy at a lower level, using the WebRequest.DefaultWebProxy property.

Here's an example:

var webProxy = new WebProxy
{
    Address = "http://myproxy:8080",
    Credentials = new NetworkCredential("username", "password")
};

WebRequest.DefaultWebProxy = webProxy;

You should then tell WCF to use the default proxy that was just configured through the WebProxy class:

<bindings>
    <customBinding>
        <binding name="MyBinding">
            <textMessageEncoding messageVersion="Soap11" />
            <httpTransport authenticationScheme="Basic"
                           useDefaultWebProxy="true" 
                           bypassProxyOnLocal="true"
                           proxyAuthenticationScheme="Basic" />
        </binding>
    </customBinding>
</bindings>

Related resources:

  • How to supply dedicated credentials for web proxy authentication in a WCF client