views:

2032

answers:

2

I have successfully created a WS client that works correctly when NOT using authentication.

However, the server (WebSphere) now requires adding a ws-security username token, and I'm having a hard time doing this. The resulting SOAP message is supposed to look something like this:

<soapenv:Envelope 
  xmlns:ns="http://foo.bar/1.0"
  xmlns:ns1="http://www.witsml.org/schemas/140"   
  xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"&gt;

  <soapenv:Header>

    <wsse:Security soapenv:mustUnderstand="1" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"&gt;
      <wsse:UsernameToken wsu:Id="UsernameToken-2" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"&gt;
        <wsse:Username>foo</wsse:Username>
        <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText"&gt;bar&lt;/wsse:Password&gt;    
        <wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary"&gt;foooooobar==&lt;/wsse:Nonce&gt;
        <wsu:Created>2010-01-25T13:09:24.860Z</wsu:Created>
      </wsse:UsernameToken>
    </wsse:Security>

  </soapenv:Header>

  <soapenv:Body>
    <ns:fooBar>...</ns:fooBar>
  </soapenv:Body>

I've downloaded and installed Microsoft's WSE 3.0 SDK and added a reference to the DLL in my Visual Studio 2005 project.

I now have access to the Microsoft.Web.Services3.* namespaces, but I'm currently stumped on how to proceed.

The client code has been generated automatically by a web reference, so I only do a minor amount of work to send the message to the server unauthenticated:

WS.FooResultHttpService ws = new WS.FooResultHttpService();
ws.Url = "http://foo.bar.baz";
ws.SendSomething(message);

I've just begun to investigate using Microsoft.Web.Services3.Security.Tokens.UsernameTokenManager, but so far I haven't been able to get anything up and running.

Any hints would be greatly appreciated, as I can't seem to find any good recipes on the net.

Thanks!

+2  A: 

Make sure your proxy class inherits from Microsoft.Web.Services3.WebServicesClientProtocol. You can do this either by changing the proxy class itself, or by generating it via the command line using wsewsdl3.exe with the /type:webClient switch.

You can then pass the credentials like this:

using Microsoft.Web.Services3;
using Microsoft.Web.Services3.Security.Tokens;
using Microsoft.Web.Services3.Security;
.
.
.
WS.FooResultHttpService ws = new WS.FooResultHttpService();
ws.RequestSoapContext.Security.Tokens.Add(new UsernameToken("blah", "blah", PasswordOption.SendPlainText));

This is what I've done in the past to get WSE3.0 going in Studio 2008. Hope that helps.

wsanville
Thanks, this was very helpful!
csl
+2  A: 

Got it working, unfortunately before reading wsanville's great answer.

To help others, I'm posting all the steps I needed to do to get it working with Visual Studio 2005:

  • Install WSE 3.0, choose custom and select everything
  • Read Implementing direct authentication with username token in WSE 3.0 for hints
  • Relaunch Visual Studio 2005, now right-click on your project in the solution explorer, and you should have a WSE Settings 3.0 menu item and use that if you want to.
  • Update your web references, this should create a new HTTP web service proxy class, with a different name, e.g. YourWsNameHttpServiceWse. This is essentially the same as running wsewsdl3.exe
  • Use this new class, and you should have access to WSE methods and properties, such as SetClientCredential.

I ended up doing almost everything in code, instead of relying on the config-files that are built with my C# DLL. The code ended up looking like this:

FooBarHttpServiceWse wse = new FooBarHttpServiceWse();

wse.SetClientCredential(new UsernameToken(
    "username",
    "password",
    PasswordOption.SendPlainText));

wse.SetPolicy(new FooBarPolicy());
wse.CallSomeServerFunction(yourRequest)

I created my own policy, which looked like this:

using Microsoft.Web.Services3.Design;

// ...

public class FooBarPolicy : Policy
{
    public FooBarPolicy()
    {
        this.Assertions.Add(new UsernameOverTransportAssertion());
    }
}

Finally, the WebSphere server responded that A required header representing a Message Addressing Property is not present, and inspecting the outgoing message (using the nice tool Fiddler) I saw the SOAP fault from the server indicated that the Action header was missing.

I tried in vain to set the wsa:Action element myself:

using Microsoft.Web.Services3.Addressing;

// ...

wse.RequestSoapContext.Addressing.Action = new Action("CallSomeServerFunction");

The problem was that even if I set an action, when it was sent over the wire, it was empty. Turned out I had to open the WSE proxy class and edit an attribute there:

[System.Web.Services.Protocols.SoapDocumentMethodAttribute(
    "---Edit this to set wsa:Action---", 
    Use=System.Web.Services.Description.SoapBindingUse.Literal, 
    ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Bare)]
// ...
public SomeServerFunction(...)

After that, it all worked out nicely.

csl