I have been all over the net for this. I've just been having a devil of a time doing it, and the vendor whose web service I'm trying to consume refuses to officially support WCF as a method of consumption.
I'm no web services expert, so I'll do my best to document and explain with this initial post, but by all means, request more information if you need it, and hopefully I'll be able to supply whatever is necessary.
The service
At my company, we use a vendor application that exposes a service. The application is written in java, and it looks like the wsdl was created with Apache Axis 1.2.
The code
My legacy code uses WSE 3.0. In particular, it uses the proxy classes that have "WSE" auto-tacked at the end. This allows me to use a much simpler authentication scheme (the only way I could get it to work). I don't need to use certificates. I use a derivative of SecurityPolicyAssertion
, and wrap it in a Policy
object that gets passed to the SetPolicy
method of the client class. Here's all I need to do to create a working instance of the client:
MyWebServiceWse api = new MyWebServiceWse();
api.Url = myUrl;
api.SetPolicy(new Policy(new MyDerivedSecurityAssertion(user, pass)));
My default, out-of-the-box code for WCF (generated with a service reference) does not accept credentials, so I know there's a problem right off the bat. I've read various things online about using different security
or binding settings in my app.config
, but nothing has ever completely worked. My most common error after copious tinkering is WSDoAllReceiver: Request does not contain required Security header
.
Here's the app.config. Perhaps we could start by telling me what ought to change here to facilitate passing the credentials--again, I've seen varying opinions online.
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="MySoapBinding" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
useDefaultWebProxy="true">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<security mode="None">
<transport clientCredentialType="None" proxyCredentialType="None"
realm="" />
<message clientCredentialType="UserName" algorithmSuite="Default" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://xyz:12345/services/MyService"
binding="basicHttpBinding" bindingConfiguration="MySoapBinding"
contract="MyNS.MyService" name="MyService" />
</client>
</system.serviceModel>
</configuration>
I have changed some of the attributes to obscure the specific service we are using (company policy and all that).
And here is the sample C# code so far (testing in a console app):
MyClient client = new MyClient();
client.listMethod();
UPDATE
Read this SO post: wcf security . . ..
I have updated my app.config accordingly, and am now passing username and pwd in code. I am still receiving the same error:
WSDoAllReceiver: Request does not contain required Security header
20120517 UPDATE
A successful request (from WSE3):
<soap:Header>
<wsa:Action>
</wsa:Action>
<wsa:MessageID>urn:uuid:cb739422-c077-4eec-8cb2-686837b76878</wsa:MessageID>
<wsa:ReplyTo>
<wsa:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:Address>
</wsa:ReplyTo>
<wsa:To>http://removed-for-security</wsa:To>
<wsse:Security soap:mustUnderstand="1">
<wsu:Timestamp wsu:Id="Timestamp-e13feaf9-33d9-47bf-ab5b-60b4611eb81a">
<wsu:Created>2012-05-17T11:25:41Z</wsu:Created>
<wsu:Expires>2012-05-17T11:30:41Z</wsu:Expires>
</wsu:Timestamp>
<wsse:UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="SecurityToken-00c26e1a-3b3b-400f-a99a-3aa54cf8c8ff">
<wsse:Username>change-to-protect-the-innocent</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">nice-try</wsse:Password>
<wsse:Nonce>KJMvUuWF2eO2uIJCuxJC4A==</wsse:Nonce>
<wsu:Created>2012-05-17T11:25:41Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
</soap:Header>
<soap:Body>
<listChannels xmlns="http://removed-for-security">
<rowfrom>0</rowfrom>
<rowto>10</rowto>
</listChannels>
</soap:Body>
</soap:Envelope>
Working on getting the WCF trace--will add shortly.
20120517 UPDATE 2
And here's the envelope from WCF:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none"></Action>
</s:Header>
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<listChannels xmlns="http://removed-for-security">
<rowfrom>1</rowfrom>
<rowto>2147483647</rowto>
</listChannels>
</s:Body>
</s:Envelope>
20120518 UPDATE I have tried implementing the solution in the post that Mike Miller links to in the comments. Now I receive the following error (no message ends up getting sent because something's barfing on the scheme):
The provided URI scheme 'http' is invalid; expected 'https'.
And in case anyone wants to ask, yes, I need to send over http, and yes, I'm aware that credentials are sent as unencrypted strings :-)
security mode="None"
its likely no soap security header is being created but the captured requests will show that. I believe the basicHttpBinding cannot be configured to support message level security. You'll likely need the wsHttpBinding for that configuration. Lastly, look at what Microsoft has done in the WCF Express Interop project, its wizards may apply to WCF clients too. – Glaciology