Friday, May 10, 2013

Eclipse creates Java Web service client with WSS name token security

This is a combo task. It illustrates an end to end scenario of creating a Web service client based on a WSDL. Then it shows a way to add WSS name token security header, which ironically is different from the previous post http://yuanmengblog.blogspot.com/2013/05/jax-ws-client-call-web-services-with-ws.html

First of all, I use the same fooPS proxy service from OSB (from the previous post). You can find the wsdl from the previous post. I use Eclipse 3.7.1. Here are the steps:

Select JavaEE perspective. Create a new Web Service Client project: File->Other->Web Service Client.

On the next screen, enter your WSDL URL, then move the slide on the left side all the way up.

Then just click finish and let Eclipse does its magic.

When all said and done, Eclipse generates these files:
  • FooBPEL.java - interface, extends Remote
  • FooBPELBindingStub.java - implments FooBPEL and extends Stub
  • FooBPELProxy.java - implements FooBPEL
  • Foobpel_client_ep.java - interface implements Service
  • Foobpel_client_epLocator.java - extends Foobpel_client_ep
I added a server in Eclipse to point to my local Weblogic.


To deploy the new project to the server (I suppose there are better ways to do it): Select Run, Run As, Run on Server, then select the ear to deploy. I only do this once. If I make changes later, i just right click on the server (Tab on the bottom), then select "Publish".

When the deployment is finished, you will get a link http://localhost:7001/WebServiceProject/sampleFooBPELProxy/TestClient.jsp (again, all of these are auto generated, i haven't done a thing yet).

As you can see there is one "process()" method. Try invoke it, it would fail, because there is no security header. For sanity check, you can go to OSB fooPS, remove the WSS security policy, then try to invoke "process()" from the test page again, it should work correctly. Otherwise, you got other problems you need to sort out first before worrying about the security. If it works, then you just succesfully created a Web service client with Eclipse.

Next, I'll show you how I managed to add the security header to the client. In fact, the change is very mimium, but it took me a while to figure it out.

Firstly, i went down a path that did not work out. In the previous post (command line java web service client), the client program relies on casting the "port" into "BindingProvider", then set the security header that way. I just couldn't make it work with the Eclipse generated classes. I couldn't find the equivalent of the "Port" class in this case. Although there is getPort() method in "Foobpel_client_epLocator" class, but I have no way to cast it into the BindingProvider.

Here is what finally made it work: go to FooBPELProxy.java, find "process()" method, make changes as below:
...
import javax.xml.namespace.QName;
import javax.xml.ws.BindingProvider;
import weblogic.wsee.security.unt.ClientUNTCredentialProvider;
import weblogic.xml.crypto.wss.provider.CredentialProvider;
import weblogic.xml.crypto.wss.WSSecurityContext;
import org.apache.axis.message.SOAPHeaderElement;
import javax.xml.soap.SOAPElement;
...

 public java.lang.String process(java.lang.String input)
   throws java.rmi.RemoteException {
  if (fooBPEL == null)
   _initFooBPELProxy();

  org.apache.axis.client.Stub stub = null;
  try {
   stub = (org.apache.axis.client.Stub) fooBPEL;
   String wsse = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
   String tstr = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText";
   SOAPHeaderElement security = new SOAPHeaderElement(wsse, "Security");
   SOAPElement usernameToken = security.addChildElement("UsernameToken");
   SOAPElement username = usernameToken.addChildElement("Username");
   username.addTextNode("weblogic");
   SOAPElement pwd = usernameToken.addChildElement("Password");
   pwd.addTextNode("welcome1");
   pwd.setAttribute("Type", tstr);
   stub.setHeader(security);
  } catch (Exception e) {
   System.out.println("exception=" + e.getMessage());
  }
  return fooBPEL.process(input);
 }

That's my hack to make it work. This may not be the way you code for production, it's just a proof of concept. 

4 comments: