Securing a webservice at method level : Apache CXF + Spring Security + JSR-250 + wss4j

Hi reader,

I was once told to write a service. It was initialy aimed to a web application. Following the YAGNI principle, so far, no need to remote anything, the service was jvm co-located.
The requirements evolved such that I the same service was need by other apps. I knew I had to remote the service. The remoting technology I’m the most familiar with and comfortable is webservice mainly thanks to Apache CXF.

I was used to configuring security with spring-security in a webapp at servlet and filter level.
With this service no logging page, no session, no filter (not exactly true but true in the way we think about them).
I found out that WSS4J was exactly designed for my need : it allows one to apply request and response interceptors. One can implement any security behaviour : transmitting credentials (client part). spring-security takes it from here : it tries to match credentials against a user repository then enables fined grained security at level method.
The absolute solution adds an HTTPS channel between client and server.
I will focus on the wss4j part, as the https part is usually configured in Apache Httpd by enabling mod_ssl, mod_rewrite, etc.

For the example I came up with those stories :
– As anonymous I should find a product by description
– As anonymous I should find a product by id
– As anonymous I should not add a product
– As anonymous I should not delete a product
– As anonymous I should not update a product

These following steps allowed me to test such stories :

– define the functionnalities to protect

..... omitted for clarity
    * @see
   @RolesAllowed( {Role.ADMIN_ROLE_ID} )
   public Long add(Product product) {

      if (product == null)
         return null;

      return persistenceManager.persist(product);

..... omitted for clarity

– define users, roles

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns=""
	xmlns:xsi="" xmlns:context=""

		jsr250-annotations="enabled" />

		<property name="targetClass"
			value="" />
		<property name="targetMethod" value="setStrategyName" />
		<property name="arguments" value="MODE_INHERITABLETHREADLOCAL" />

			<sec:user-service id="userDetailsService">
				<sec:user name="anonymous" password="anonymous" authorities="ROLE_ANY" />
				<sec:user name="admin" password="*password@0" authorities="ROLE_ADM" />

– activate protections against roles

	<jaxws:endpoint id="productServiceWsEndpoint"
		implementor="#productService" address="/ProductService">
			<ref bean="jaxws-service-factory" />

			<bean class="org.apache.cxf.binding.soap.saaj.SAAJInInterceptor" />
			<bean class="">
						<entry key="action" value="UsernameToken" />
						<entry key="passwordType" value="PasswordText" />
							<ref bean="serverPasswordMatcherHandler" />



import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

 * @author
public class ServerPasswordMatcherHandler implements CallbackHandler {

	public static final String BEAN_ID = "serverPasswordMatcherHandler"; 
	private AuthenticationManager providerManager;
	 * @see[])
	public void handle(Callback[] callbacks) throws IOException,
			UnsupportedCallbackException {
		WSPasswordCallback pc = null;
		for (Callback callback : callbacks) {
			if (callback instanceof WSPasswordCallback) {
				pc = (WSPasswordCallback)callback; break;
		if (providerManager == null) throw new IllegalStateException("authenticationProvider should've been wired");
		if (pc != null && StringUtils.hasText(pc.getIdentifier())) {
			Authentication authentication  = providerManager.authenticate(new UsernamePasswordAuthenticationToken(pc.getIdentifier(), pc.getPassword()));



– test

	public void anonymousShouldNotDelete() {

		String name = "doliprane";

		String description = "Ce médicament est un antalgique et un antipyrétique qui contient du paracétamol."
			+ "\nIl est utilisé pour faire baisser la fièvre et dans le traitement des affections douloureuses.";

		Long productId = addProduct(name, description, ADMIN_LOGIN, ADMIN_PASSWORD);

		securedService = getSecuredProxy(ANONYMOUS_LOGIN, ANONYMOUS_PASSWORD);


	private ProductService getSecuredProxy(String login, final String password) {
		JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
		List<Interceptor> inInterceptors = new ArrayList<Interceptor>();
		inInterceptors.add(new LoggingInInterceptor());
		List<Interceptor> outInterceptors = new ArrayList<Interceptor>();
		outInterceptors.add(new LoggingOutInterceptor());
		outInterceptors.add(new SAAJOutInterceptor());
		final Map<String, Object> authConfig = new HashMap<String, Object>();

		authConfig.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
		authConfig.put(WSHandlerConstants.USER, login);
		authConfig.put(WSHandlerConstants.PASSWORD_TYPE, "PasswordText");

		authConfig.put(WSHandlerConstants.PW_CALLBACK_REF, new CallbackHandler()
			public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException
				WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];

		outInterceptors.add(new WSS4JOutInterceptor(authConfig));

		return (ProductService) factory.create();


I did not have time to :
– use spring built-in sha password encoding system
– implement real anonymous : no security check when credentials are not provided.

You can dwnload the full source code here




11 thoughts on “Securing a webservice at method level : Apache CXF + Spring Security + JSR-250 + wss4j

  1. Hi,

    Im having the problem that my pc.getPassword() is empty in my server callback although I do send a password. Can you perhaps help me out here. My callback looks exaclty like yours and my endpoint config looks like:

  2. Hopefully now the code will show:

    [jaxws:endpoint id=”calendarWebService” implementor=”#calendarService”

    [bean class=”org.apache.cxf.binding.soap.saaj.SAAJInInterceptor” /]
    [bean class=””]
    [entry key=”action” value=”UsernameToken Timestamp” /]
    [entry key=”passwordType” value=”PasswordDigest” /]
    [entry key=”passwordCallbackRef”]
    [ref bean=”customPasswordCallback” /]

    Seems like it doesnt like the greater then signs

  3. The probleem seems to be with the passwordType. When i change it to PasswordText, i do get the client password and it works fine. When i set it to PasswordDigest, the pc.getPassword() returns null.

    Do you have a clue how to fix this?

  4. No problem…it works now and by the looks of it, password digest is indeed not supported yet. Not a real issue as we are using SSL. Thanks for your article though!

  5. Hi Louis,

    As you mentioned Basic auth setup is pretty strait forward.
    How do I setup digest auth type to webservice client using cxf ?
    As of now my client by default assumes Basic auth type when I set the username password in the factory object. And the request message header have “Authentication [Basic: ……]” in it.
    I want to setup a cxf client for digest auth. could you please assist ?

    Many thanks

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s