Adding WS-Security over soap using Apache Camel
WS-Security (Web Services Security) is a protocol which allows you to secure your soap web services. The client, who makes the soap request, has to provide a login and a password in the soap header.
The server receives the soap request, checks the credentials and validates or not the request. With Apache Camel, it is easy to work with soap web services (especially if you use Apache CXF), but handling with WS-Security can be tricky.
The idea is to create an xml template with all required information (including login and password) and add the template to the soap header.
public void addSoapHeader(Exchange exchange,String soapHeader){ List<SoapHeader> soapHeaders = CastUtils.cast((List<?>) exchange.getIn().getHeader(Header.HEADER_LIST)); SoapHeader newHeader; if(soapHeaders == null){ soapHeaders = new ArrayList<SoapHeader>(); } try { newHeader = new SoapHeader(new QName("soapHeader"), DOMUtils.readXml(new StringReader(soapHeader)).getDocumentElement()); newHeader.setDirection(Direction.DIRECTION_OUT); soapHeaders.add(newHeader); exchange.getIn().setHeader(Header.HEADER_LIST, soapHeaders); } catch (Exception e) { //log error } }
Apache Camel uses an Exchange
interface which has methods to retrieve or update headers. The soapHeader
parameter is a string containing the xml template.
We retrieve the current headers and we add a new header named soapHeader
. We convert the soapHeader
property from string to XML thanks to the DOMUtils
class.
The newHeader.setDirection(Direction.DIRECTION_OUT)
instruction means that the header will be applied to a request either leaving a consumer endpoint or entering a producer endpoint (that is, it applies to a WS request message propagating through a route).
Now let’s create the xml template and call the addSoapHeader
method:
public void addWSSESecurityHeader(Exchange exchange,String login,String password){ String soapHeader = "<?xml version=\"1.0\" encoding=\"utf-8\"?><wsse:Security xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\"+ "xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\"><wsse:UsernameToken wsu:Id=\"UsernameToken-50\"><wsse:Username>" + login + "</wsse:Username><wsse:Password Type=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText\">" + password + "</wsse:Password></wsse:UsernameToken></wsse:Security>"; //Add wsse security header to the exchange addSoapHeader(exchange, soapHeader); }
As we can see, we need two namespaces in our xml (to handle with WS-Security):
- http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd
- http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd
We can then use interesting tags into our xml:
- wsse :UsernameToken : Includes both username and password information
- wsse :Username : Username required for authentication
- wsse :Password : Password required for authentication
Next we just have to call our method addSoapHeader
to add our xml into the soap header. Here’s the full code with a complete Apache Camel route:
package com.example.test; import java.io.StringReader; import java.util.ArrayList; import java.util.List; import javax.xml.namespace.QName; import org.apache.camel.Exchange; import org.apache.camel.util.CastUtils; import org.apache.cxf.binding.soap.SoapHeader; import org.apache.cxf.headers.Header; import org.apache.cxf.headers.Header.Direction; import org.apache.cxf.helpers.DOMUtils; public class MyRoute extends RouteBuilder { public void addSoapHeader(Exchange exchange,String soapHeader){ List<SoapHeader> soapHeaders = CastUtils.cast((List<?>) exchange.getIn().getHeader(Header.HEADER_LIST)); SoapHeader newHeader; if(soapHeaders == null){ soapHeaders = new ArrayList<SoapHeader>(); } try { newHeader = new SoapHeader(new QName("soapHeader"), DOMUtils.readXml(new StringReader(soapHeader)).getDocumentElement()); newHeader.setDirection(Direction.DIRECTION_OUT); soapHeaders.add(newHeader); exchange.getIn().setHeader(Header.HEADER_LIST, soapHeaders); } catch (Exception e) { //log error } } public void addWSSESecurityHeader(Exchange exchange,String login,String password){ String soapHeader = "<?xml version=\"1.0\" encoding=\"utf-8\"?><wsse:Security xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\"+ "xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\"><wsse:UsernameToken wsu:Id=\"UsernameToken-50\"><wsse:Username>" + login + "</wsse:Username><wsse:Password Type=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText\">" + password + "</wsse:Password></wsse:UsernameToken></wsse:Security>"; //Add wsse security header to the exchange addSoapHeader(exchange, soapHeader); } @Override public void configure() throws Exception { from("endpointIn") .process(new Processor(){ @Override public void process(Exchange exchange) throws Exception { addWSSESecurityHeader(exchange, "login","password"); } }) .to("endointOut") ; } }
Hi Michael,
Thanks for the article. I’ve been trying to follow what you posted, but after the following lines i can see during debugging that inside newHeader wsse:security is null:
newHeader = new SoapHeader(new QName(“soapHeader”), DOMUtils.readXml(new StringReader(soapHeader)).getDocumentElement());
28
newHeader.setDirection(Direction.DIRECTION_OUT);
Do i need to do something else, such as add some dependencies etc?
The error that i get while submiting the reqest to the endpoint is: “None of the policy alternatives can be satisfied”
Add following jar dependencies : cxf-rt-ws-security , cxf-rt-ws-policy