Apache CXF Tutorial - WS-Security with Spring
This tutorial will cover adding an authentication component to your web service though WS-Security. If you need an overview of how to setup CXF then you may find our previous tutorial helpful. Another helpful resource is CXF’s own WS-Security tutorial. However, it does not include information on how to setup the client through Spring.
To begin with, make sure you have at least the following .jars in addition to the required base CXF .jars:
spring-beans-2.0.6.jar
spring-context-2.0.6.jar
spring-core-2.0.6.jar
spring-web-2.0.6.jar
wss4j-1.5.1.jar
xmlsec-1.3.0.jar
Now we will add a security interceptor to the server’s Spring configuration file, which we named cxf.xml in the last tutorial in order to match the CXF documentation.
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jaxws="http://cxf.apache.org/jaxws"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://cxf.apache.org/jaxws
http://cxf.apache.org/schemas/jaxws.xsd">
<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml"/>
<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
<jaxws:endpoint id="auth"
implementor="com.company.auth.service.AuthServiceImpl"
address="/corporateAuth">
<jaxws:inInterceptors>
<bean class="org.apache.cxf.binding.soap.saaj.SAAJInInterceptor" />
<bean class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">
<constructor-arg>
<map>
<entry key="action" value="UsernameToken" />
<entry key="passwordType" value="PasswordText" />
<entry key="passwordCallbackClass" value="com.company.auth.service.ServerPasswordCallback" />
</map>
</constructor-arg>
</bean>
</jaxws:inInterceptors>
</jaxws:endpoint>
</beans>
You can change the action and passwordType to do more advanced authentication. In this example, we will simply require all authenticating clients to know a single password specified by the server. If you’d like each client to have it’s own password you can specify that in the callback, which is the next thing we must implement:
package com.company.auth.service;
import java.io.IOException;
import java.util.ResourceBundle;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import org.apache.ws.security.WSPasswordCallback;
public class ServerPasswordCallback implements CallbackHandler {
private static final String BUNDLE_LOCATION = "com.company.auth.authServer";
private static final String PASSWORD_PROPERTY_NAME = "auth.manager.password";
private static String password;
static {
final ResourceBundle bundle = ResourceBundle.getBundle(BUNDLE_LOCATION);
password = bundle.getString(PASSWORD_PROPERTY_NAME);
}
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];
// Set the password on the callback. This will be compared to the
// password which was sent from the client.
// We can call pc.getIdentifer() right here to check the username
// if we want each client to have it's own password.
pc.setPassword(password);
}
}
The server is now setup to require a password. The password we are requiring is one that we specified in a properties file and then read in through a ResourceBundle. You may find it easier to simply hard code the password on the initial run and then replace it with your own means of authentication once the service is up and running.
If you are running on WebLogic 9, as I was, then you will get an error “java.lang.UnsupportedOperationException: This class does not support SAAJ 1.1“. In order to correct that, make sure your version of the SAAJ classes are being used by adding the following to your weblogic.xml descriptor file:
<container-descriptor>
<prefer-web-inf-classes>true</prefer-web-inf-classes>
</container-descriptor>
You WebLogic folks must also then set two properties in your WebLogic JDK:
-Djavax.xml.soap.MessageFactory=com.sun.xml.messaging.saaj.soap.ver1_1.SOAPMessageFactory1_1Impl
-Djavax.xml.soap.SOAPConnectionFactory=weblogic.wsee.saaj.SOAPConnectionFactoryImpl
We now have to setup the client to supply a password. Firstly, we will create another Spring file at com/company/auth/service/cxfClient.xml to setup the application context for the client:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jaxws="http://cxf.apache.org/jaxws"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">
<bean id="proxyFactory" class="org.apache.cxf.jaxws.JaxWsProxyFactoryBean">
<property name="serviceClass" value="com.company.auth.service.AuthService"/>
<property name="address" value="http://localhost:7001/authManager/services/corporateAuth"/>
<property name="inInterceptors">
<list>
<ref bean="logIn" />
</list>
</property>
<property name="outInterceptors">
<list>
<ref bean="logOut" />
<ref bean="saajOut" />
<ref bean="wss4jOut" />
</list>
</property>
</bean>
<bean id="client" class="org.apache.cxf.jaxws.JaxWsProxyFactoryBean" factory-bean="proxyFactory" factory-method="create" />
<bean id="logIn" class="org.apache.cxf.interceptor.LoggingInInterceptor" />
<bean id="logOut" class="org.apache.cxf.interceptor.LoggingOutInterceptor" />
<bean id="saajOut" class="org.apache.cxf.binding.soap.saaj.SAAJOutInterceptor" />
<bean id="wss4jOut" class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor">
<constructor-arg>
<map>
<entry key="action" value="UsernameToken" />
<entry key="user" value="ws-client" />
<entry key="passwordType" value="PasswordText" />
<entry key="passwordCallbackClass" value="com.company.auth.service.ClientPasswordCallback" />
</map>
</constructor-arg>
</bean>
</beans>
We then need to set the password for our message:
package com.company.auth.service;
import java.io.IOException;
import java.util.ResourceBundle;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import org.apache.ws.security.WSPasswordCallback;
public class ClientPasswordCallback implements CallbackHandler {
private static final String BUNDLE_LOCATION = "com.company.auth.authClient";
private static final String PASSWORD_PROPERTY_NAME = "auth.manager.password";
private static String password;
static {
final ResourceBundle bundle = ResourceBundle.getBundle(BUNDLE_LOCATION);
password = bundle.getString(PASSWORD_PROPERTY_NAME);
}
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];
// set the password for our message.
pc.setPassword(password);
}
}
Finally, we create the service factory, which is extremely easy since all the work was done in the Spring file:
package com.company.auth.service;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public final class AuthServiceFactory {
private static final ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[] {
"com/company/auth/service/cxfClient.xml"
});
public AuthServiceFactory() {
}
public AuthService getService() {
return (AuthService) context.getBean("client");
}
}
Congratulations. Your web service now utilizes a basic implementation of WS-Security. Hopefully, that will be enough background to get you on your way.
Buy Me a BeerTags: CXF, Java, Security, SOA, Spring, Tutorial, Web Services, WebLogic











Web Services Tutorial with Apache CXF | Lumidant said,
February 19, 2008 at 5:23 pm
[...] you are looking for something to learn next then may I suggest our tutorial on adding security to your web service. That tutorial will also show you how to setup the client using Spring, which you may find [...]
Irshad said,
February 23, 2008 at 8:19 am
Thanks,
Can you help us using acegi with CXF with an example.
–Irshad.
Ben said,
February 25, 2008 at 4:10 pm
Hi Irshad,
I would love to, but unfortunately I haven’t gotten a chance to use Acegi yet. Maybe in the near future.
-Ben
Ryan said,
March 4, 2008 at 8:54 pm
Do we need to implement this class: com.company.auth.service.ClientPasswordCallback or is it generated by CXF or Spring somehow? Also, did you re-use the client class from the first example (with a modified target address)?
My attempt at this example is close but the WS-Security part isn’t working from the client side. For what it’s worth, here is my error information as my implementation stands:
INFO: Inbound Message
—————————-
Encoding: UTF-8
Headers: {content-type=[text/xml;charset=UTF-8], connection=[close], Date=[Wed, 05 Mar 2008 00:59:24 GMT], Content-Length=[245],
Server=[Apache-Coyote/1.1]}
Messages:
Message:
Payload: soap:ClientRequest does not contain required Security header.
————————————–
Exception in thread “main” javax.xml.ws.soap.SOAPFaultException: Request does not contain required Security header.
at org.apache.cxf.jaxws.JaxWsClientProxy.invoke(JaxWsClientProxy.java:175)
at $Proxy23.getEmployee(Unknown Source)
at com.company.auth.client.Client.main(Client.java:25)
Caused by: org.apache.cxf.binding.soap.SoapFault: Request does not contain required Security header.
at org.apache.cxf.binding.soap.interceptor.Soap11FaultInInterceptor.handleMessage(Soap11FaultInInterceptor.java:70)
at org.apache.cxf.binding.soap.interceptor.Soap11FaultInInterceptor.handleMessage(Soap11FaultInInterceptor.java:35)
at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:208)
at org.apache.cxf.interceptor.AbstractFaultChainInitiatorObserver.onMessage(AbstractFaultChainInitiatorObserver.java:96)
Ben said,
March 10, 2008 at 12:34 am
Ah, yes. You are quite right, Ryan. I’m sorry for leaving that out of the tutorial the first go around. It’s been updated now.
I did not re-use the client class from the first example, but instead used the cxfClient.xml file to setup the client through Spring in this example.
patrick van amstel said,
April 16, 2008 at 1:32 am
Lovely tutorial.
Can you add a zip file with all classes and configuration included? That would be very helpful.
grt Patrick
Glen said,
May 7, 2008 at 9:45 am
Ben, I’m confused on something–where did you tie the AuthServiceFactory class you created at the bottom of your tutorial to the creation of client proxies? I.e., how does Spring know to go to that class in particular to generate the SOAP client? The proxyConfig bean in the app context config file (below) is not explicitly referencing AuthServiceFactory, so I don’t know how Spring realizes it needs to call AuthServiceFactory to generate the client proxies.
….
Thanks,
Glen
Ben said,
May 7, 2008 at 12:36 pm
Hi Glen,
I am not instantiating the AuthServiceFactory through Spring in this example, but rather am using it only to instantiate the AuthService itself. For example, the following should work:
final AuthServiceFactory f = new AuthServiceFactory():
final AuthService service = f.getService();
Tony Murphy said,
May 14, 2008 at 3:52 am
Hi,
I’ve being looking at spring and cxf and then noticed this presentation on parleys, interesting. suggests that restful webservices are the way to go
http://www.parleys.com/display/PARLEYS/REST%20-%20The%20Better%20Web%20Services%20Model
Ckarthi said,
May 14, 2008 at 10:52 pm
Really very good tutorial. [The CXF overview tutorial was too good that using Tomcat i was able to try it out in just 5 minutes.]
Reut said,
June 25, 2008 at 8:11 am
It seems to me that the code for the ServerPasswordCallback is displayed twice, and the code where the client sets the password before caliing the srvice doesn’t appear at all.
am i correct?
Ben said,
July 1, 2008 at 1:42 pm
Hi Reut,
I only see the ServerPasswordCallback displayed once in the article. ClientPasswordCallback is also displayed, which is where the client sets the password.
-Ben
Wes said,
July 7, 2008 at 1:07 pm
Hello Reut,
I have a question for you… I’m running CXF with WSS4J (currently in Jetty6 for testing). I can create the service and validate the password text with username token profile successfully using SOAPUI. However, when I create the client (I’m using Struts2 also tested from a jetty container) I get the following error…
javax.xml.soap.SOAPException: Unable to create message factory for SOAP: Provider org.apache.axis.soap.MessageFactoryImpl not found
The strange part is that if I call the service directly with a test case (even through an action test case) everything works. It’s only when I actually call it inside the jetty container that it fails. Notice… I’m not using axis at all either. That has me confused. Do you have any ideas?
W
Ben said,
July 7, 2008 at 3:46 pm
Hi Wes,
Your problem sounds like a classpath issue. I’m not familiar with Jetty, but I would suggest looking into whether there is some way that you can print out the classpath and make sure that all the .jars that you need are present.
-Ben
Wes said,
July 8, 2008 at 11:17 pm
Hi Ben,
The same thing drove me crazy. I’m using maven and with its transitive dependency management I wasn’t sure if I really was getting a jar from axis in my classpath (even tho I’m using cxf). I ultimately did figure it out thanks to another guy on our team (thx JR). It goes back to the same “WebLogic” issue above…
-Djavax.xml.soap.MessageFactory=com.sun.xml.messaging.saaj.soap.ver1_1.SOAPMessageFactory1_1Impl
-Djavax.xml.soap.SOAPConnectionFactory=weblogic.wsee.saaj.SOAPConnectionFactoryImpl
I had to define the com.sun factory for my implementation. Otherwise, it apparently defaults to axis.
As for jetty, it’s create with the maven plugin. Just build and type mvn jetty6:run and you have a container to test your web app in. Very, very nice. Glassfish v3 will be able to do the same.
Thank you for responding. I spend hours chasing this issue.
Wes
Tom said,
July 10, 2008 at 7:56 am
Hi Ben and thx for this tutorial.
For me the web service works and returns the HelloWorld string but doesn’t care of WS Security. I fixed different users on server and client without visible effect. And println insert in both PasswordCallback are not displayed.
That’s strange because it works fine with CXF native frontend, but I cannot find what’s wrong with Spring and WAS.
Did you encounter such problem?
Tom said,
July 10, 2008 at 8:14 am
Forget my previous post, I made a noob mistake in my xml client file. :p
Tom said,
July 10, 2008 at 9:32 am
Re,
This time I’m really stucked… My WS works fine with Spring and WAS 6.1, but when I try to add WS-Security authentication I get this error:
org.w3c.dom.DOMException: HIERARCHY_REQUEST_ERR: An attempt was made to insert a node where it is not permitted.
at org.apache.xerces.dom.CoreDocumentImpl.insertBefore(Unknown Source)
at org.apache.xerces.dom.NodeImpl.appendChild(Unknown Source)
at com.ibm.ws.webservices.engine.xmlsoap.SOAPPart.appendChild(SOAPPart.java:244)
at org.apache.cxf.staxutils.W3CDOMStreamWriter.newChild(W3CDOMStreamWriter.java:81)
at org.apache.cxf.staxutils.W3CDOMStreamWriter.writeStartElement(W3CDOMStreamWriter.java:98)
I dont understand in that WSS works with CXF native frontend. May it be a conflict between CXF and Websphere libraries?
Ben said,
July 10, 2008 at 1:44 pm
Hey Tom,
Check out this link for some WS-specific solutions: http://cwiki.apache.org/CXF20DOC/appserverguide.html#AppServerGuide-Websphere
-Ben
Mike Lou said,
July 31, 2008 at 1:56 pm
Hi Ben,
This is a great tutorial. I have a question on server/client config. If I want to config multiple endpoints in the same config files, is there any way to secure some endpoints and leave the others public (not secure)?
-mike
Ben said,
August 4, 2008 at 2:29 pm
Hi Mike,
The interceptors are setup on a per endpoint basis, so you shouldn’t have any trouble doing that. Simply copy everything between the jaxws:endpoint tags to create a new endpoint and leave out the wss4j interceptor for the endpoints you don’t want to secure.
-Ben
Av said,
August 21, 2008 at 3:13 pm
Nice tutorial.
Do you know if there is a preferred method of authorizing a web service using cxf? I mean, after authentication, one needs to ensure that the specified user is actually authorized to execute the web service method in question. Does cxf offer any support for this, do you know?
Thanks.
Blair said,
August 29, 2008 at 10:29 am
Hello,
This is a great tutorial. Does anyone know if a tutorial like this one exists for a JAXRS (RESTful) service. In my service I pass simple XML back and forth. I try using what is shown here in this tutorial and get a bunch of SOAP errors.
Thanks.