I had some annoying problems with the WS security examples found on the net. I had an application which simply refused to pass along the header data as they should. I followed every tutorial available and did like this;
| bp.getRequestContext().put(javax.xml.ws.BindingProvider.USERNAME_PROPERTY, "admin"); |
| bp.getRequestContext().put(javax.xml.ws.BindingProvider.PASSWORD_PROPERTY,"adminpwd") |
So I had to make the header of the request myself. This is how I did it. As always, even though it doesn't matter I made it in Eclipse building it in Maven.
| Server files |
On the server side a made on class, call it Service.
@WebService
@HandlerChain(file="MyChain.xml")
public class Service {
@WebMethod
public String getName(){
return "King Kong";
}
}
Note that the file path is relative so for simplicity I just put the file (MyChain.xml) in the same directory as this file, i.e. in the source package.
Thexml file just contain a pointer towards a handler.
Now we only lack the handler class.
*****************************************************
package com.aja.util;
/**
import java.io.PrintStream;
import java.util.Map;
import java.util.Set;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
import java.io.PrintStream;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.SOAPPart;
import javax.xml.ws.handler.Handler;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPMessageContext;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class MySOAPHandler implements SOAPHandler
{
private static final String WS_SEC = "wsse:Security";
private static final String WSSE_USERNAME = "wsse:Username";
private static final String WSSE_PASSWORD = "wsse:Password";
protected PrintStream out = System.out;
protected String HandlerName = "Handler";
@PostConstruct
public void init()
{
}
@PreDestroy
public void destroy()
{
}
/**
* {@inheritDoc}
*/
public void close(@SuppressWarnings("unused")
MessageContext context)
{
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
public boolean handleMessage(SOAPMessageContext messagecontext)
{
try
{
SOAPMessage message = messagecontext.getMessage();
SOAPPart part = message.getSOAPPart();
SOAPEnvelope envelope = part.getEnvelope();
SOAPHeader header = envelope.getHeader();
String username;
String password;
Element sec = null;
for (Iterator
{
Element element = k.next();
if (element.getNodeName().equals(WS_SEC))
{
sec = element;
NodeList e = sec.getElementsByTagName(WSSE_USERNAME);
Node tok = e.item(0);
NodeList child = tok.getChildNodes();
if (child != null)
{
username = child.item(0).getNodeValue();
if (!(username.equals("admin")))
{
return false;
}
}
NodeList f = sec.getElementsByTagName(WSSE_PASSWORD);
Node t = f.item(0);
NodeList c = t.getChildNodes();
if (c != null)
{
password = c.item(0).getNodeValue();
if (!(password.equals("adminpwd")))
{
return false;
}
}
}
}
}
catch (Exception e)
{
return false;
}
return true;
}
/**
* Subclassing children of this method will determine if to process the
* request.
*
* @return True if we are to continue with authentication, false otherwise.
*/
protected boolean doAuthentication()
{
return true;
}
/**
* When the userid and password is extracted from the request it will call
* this method and return the value returned by this method.
*
* @param userName
* The username extracted from the request.
* @param password
* The password extracted from the request.
* @return See method description on this method.
*/
protected boolean isValidAuthentication(String userName, String password)
{
return true;
}
/**
* {@inheritDoc}
*/
public Set
{
// TODO Auto-generated method stub
return null;
}
/**
* {@inheritDoc}
*/
public boolean handleFault(SOAPMessageContext arg0)
{
// TODO Auto-generated method stub
return false;
}
}
***************************************************
This method can be tested by SOAP UI by sending this request, try to change the pwd and you will be rejected.
*****************************************************
********************************************************
| Client files |
Make a similar handler file, I don't use the same sice the client is separate from the server. Call it ClientHandler.xml
****************
******************************************
Make the handler file, Handler.java.
*******************************************
package com.aja.client;
import java.util.Set;
import javax.xml.namespace.QName;
import javax.xml.soap.Name;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPFactory;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPHeaderElement;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.SOAPPart;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
public class Handler implements SOAPHandler
{
private static final String WSSE = "wsse";
private static final String WS_SEC = "Security";
private static final String USERNAME_TOKEN = "UsernameToken";
private static final String USERNAME = "Username";
private static final String PASSWORD = "Password";
private static final String NAME_SPACE_URI = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
public SecurityHandler()
{
}
public Set
{
return null;
}
public boolean handleFault(SOAPMessageContext messageContext)
{
return true;
}
public boolean handleMessage(SOAPMessageContext messageContext)
{
secureClient(messageContext);
return true;
}
public void close(MessageContext messageContext)
{
}
private void secureClient(SOAPMessageContext messageContext)
{
try
{
SOAPMessage message = messageContext.getMessage();
SOAPPart part = message.getSOAPPart();
SOAPEnvelope envelope = part.getEnvelope();
SOAPHeader header = envelope.getHeader();
if (header == null)
{
header = envelope.addHeader();
}
SOAPFactory factory = SOAPFactory.newInstance();
Name security = factory.createName(WS_SEC, WSSE, NAME_SPACE_URI);
SOAPHeaderElement orderHeader = header.addHeaderElement(security);
Name userNameToken = factory.createName(USERNAME_TOKEN, WSSE,
NAME_SPACE_URI);
Name user = factory.createName(USERNAME, WSSE, NAME_SPACE_URI);
Name pwd = factory.createName(PASSWORD, WSSE, NAME_SPACE_URI);
SOAPElement elem = orderHeader.addChildElement(userNameToken);
SOAPElement userElement = elem.addChildElement(user);
userElement.addTextNode("admin");
SOAPElement pwdElement = elem.addChildElement(pwd);
pwdElement.addTextNode("adminpwd");
}
catch (Exception e)
{
System.out.println("Exec " + e.getMessage());
}
}
}
****************************************************************
And finally hook up to the file every time a web service request is called. I added this where I receive my web service client.
********************************************
@HandlerChain(file="ClientHandler.xml")
public class MyClient {
}
************************************************
In the example above the manager is an instance of my web service cleint part. This is autogenerated by maven with the wsgen and wsimport commands.
0 comments:
Post a Comment