Category Archives: xfire

CXF WS-Security using JSR 181 + Interceptor Annotations (XFire Migration)

I had blogged about how to setup XFire with WS-Security a while ago and since then the XFire 1.x series as we know it is dead, instead Apache’s CXF can be considered XFire 2.0. CXF improves over XFire in many areas including better architecture and more importantly easier message handling and manipulation. In this entry, we’ll setup a CXF application that secures its services using CXF’s WS-Security features.

Before I get to the example I want to mention some of the major changes that I noticed in CXF:

  • Interceptors instead of handlers: Handlers are out and are replaced with a much more common concept of interceptors (if you’re from the Struts2/Webwork world you know what they are). Interceptors are created by extending the AbstractPhaseInterceptor class which forces you to implement handleMessage(Message). Note that you must specify the phase where you want each interceptor executed.
  • Access to MessageContext: In XFire, the MessageContext was always available in your handlers. In CXF you don’t have access to it in the interceptor but you can get contextual properties using the message.getContextualProperty(String) methods. Access to the MessageContext is also available using the @Resource annotation as described here but this only works in service implementations.
  • JAXWS Endpoints: Spring service classes must be exposed using the <jaxws:endpoint> tag. You can also do this programmatically but why would you.
  • Message and Exchange: The Message and Exchange objects have changed dramatically from XFire. CXF’s Message and Exchange objects are devoid of all those helpful methods that were present in XFire but they make up for it by having get(Class) and get(String) methods which can be used to retrieve valuable information. It’s probably a good idea to look through Exchange.keySet() and Message.keySet() and see whats available to you in your interceptors. For example when a Fault occurs, the message.get(Exception.class) returns the exception that was thrown to cause the fault. Also note that the information present in these maps varies depending on what Phase you’re in.

Now to the WS-Security example. CXF just added support for configuring interceptors using annotations which means configuring WS-Security for our web services just got easier.

Here’s the service interface and implementation.

@WebService
public interface SportsService {
    public String getTeam();
        return "Arsenal";
    }
}

@WebService(
    serviceName="SportsService",
    endpointInterface="ca.utoronto.sis.cxfapp.SportsService")
@InInterceptors(interceptors={
        "com.arsenalist.cxfapp.WSSecurityInterceptor"
})
public class SportsServiceImpl implements SportsService {

    public String getTeam() {
        return "Arsenal";
    }
}

I’ve added a single in interceptor called WSSecurityInterceptor which is a class that we’ll write. In XFire we also needed a DomInHandler and a DomOutHandler for each service implementation, none of that is required here. WSSecurityInterceptor will just wrap the WSS4JInInterceptor class, the reason we can’t just specify WSS4JInInterceptor as an annotation is because we need to set custom properties on it such as using UsernameToken authentication.

The other thing WSSecurityInterceptor does is add a ValidateUserTokenInterceptor which is similar to ValidateUserTokenHandler in the XFire examples. Since WSS4J validates a UsernameToken only if it finds a security header we need to cover the case where no security header is specified. ValidateUserTokenInterceptor just makes sure the username, password, nonce and timestamp are specified before even considering it a valid request. Here’s the WSSecurityInterceptor and the PasswordHandler class:

public class WSSecurityInterceptor extends AbstractPhaseInterceptor {

    public WSSecurityInterceptor() {
        super(Phase.PRE_PROTOCOL);
    }
    public WSSecurityInterceptor(String s) {
        super(Phase.PRE_PROTOCOL);
    }

    public void handleMessage(SoapMessage message) throws Fault {

        Map props = new HashMap();
        props.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
        props.put(WSHandlerConstants.PW_CALLBACK_REF, new PasswordHandler());

        WSS4JInInterceptor wss4jInHandler = new WSS4JInInterceptor(props);
        ValidateUserTokenInterceptor userTokenInterceptor = new ValidateUserTokenInterceptor(Phase.POST_PROTOCOL);

        message.getInterceptorChain().add(wss4jInHandler);
        message.getInterceptorChain().add(new SAAJInInterceptor());
        message.getInterceptorChain().add(userTokenInterceptor);
    }
}

public class PasswordHandler implements CallbackHandler {
    public void handle(Callback[] callbacks) throws IOException,
            UnsupportedCallbackException {
        WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];
        if (pc.getIdentifer().equals("arsenal")) {
            pc.setPassword("gunners");
        }
    }
}

Note that the ValidateUserTokenInterceptor is invoked in a later phase than WSS4JInterceptor. Here’s the ValidateUsertokenInterceptor class:

public class ValidateUserTokenInterceptor extends AbstractPhaseInterceptor {

    public ValidateUserTokenInterceptor(String s) {
        super(s);
    }

    public void handleMessage(SoapMessage message) throws Fault {
        boolean userTokenValidated = false;
        Vector result = (Vector) message.getContextualProperty(WSHandlerConstants.RECV_RESULTS);
        for (int i = 0; i &lt; result.size(); i++) {
            WSHandlerResult res = (WSHandlerResult) result.get(i);
            for (int j = 0; j &lt; res.getResults().size(); j++) {
                   WSSecurityEngineResult secRes = (WSSecurityEngineResult) res.getResults().get(j);
                    WSUsernameTokenPrincipal principal = (WSUsernameTokenPrincipal) secRes
                            .getPrincipal();

                    if (!principal.isPasswordDigest() ||
                            principal.getNonce() == null ||
                            principal.getPassword() == null ||
                            principal.getCreatedTime() == null) {
                        throw new RuntimeException("Invalid Security Header");
                    } else {
                        userTokenValidated = true;
                    }
                }
            }
        }
        if (!userTokenValidated) {
            throw new RuntimeException("Security processing failed");
        }
    }
}

Now we have a service implementation annotated in a way where it is WS-Security enabled and is also registered as a web service. The final step remaining is to expose it as a consumable endpoint. That can be achieved by either of the following ways:

Using the fully qualified class name:

<jaxws:endpoint
   id="helloWorld"
   implementor="com.arsenalist.cfxapp.SportsServiceImpl"
   address="/SportsService" />

Or by referring to a Spring bean corresponding to the @WebService annotated class. This would be more prudent if you’re using Dependency Injection in your service implementations.

<bean id="sportsServiceImpl" class="com.arsenalist.cfxapp.SportsServiceImpl"/>

<jaxws:endpoint
   id="sportsService"
   implementor="#sportsServiceImpl"
   address="/SportsService" />

Finally you could also just use good ‘ol fashioned Spring beans. This comes in handy if you wan to specify a different binding like Aegis:

<bean id="aegisBean" class="org.apache.cxf.aegis.databinding.AegisDatabinding"/>
<bean class="org.apache.cxf.frontend.ServerFactoryBean" init-method="create">
	<property name="serviceBean" ref="registrationSoapService"/>
	<property name="address" value="/services/1_0_0/Registration"/>
	<property name="dataBinding" ref="aegisBean"/>
</bean>

The CXF documentation which shows how to configure the web.xml is pretty straightforward.

If you’re using Maven, the dependencies section of the pom.xml might look something like this. Remember that support for configuring interceptors via annotations was just added so you have to access the SNAPSHOT repositories instead of the main one.

<repositories>
    <repository>
        <id>apache-snapshots</id>
        <name>Apache SNAPSHOT Repository</name>
        <url>http://people.apache.org/repo/m2-snapshot-repository/</url>
        <snapshots>
        <enabled>true</enabled>
        </snapshots>
    </repository>
    <repository>
        <id>apache-incubating</id>
        <name>Apache Incubating Repository</name>
        <url>http://people.apache.org/repo/m2-incubating-repository/</url>
    </repository>
</repositories>
<dependencies>
    ...
    <!-- spring beans, core, context, web 2.0.6 -->
    ...
    <dependency>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-rt-transports-http</artifactId>
        <version>2.1-incubator-SNAPSHOT</version>
    </dependency>
    <dependency>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-rt-core</artifactId>
        <version>2.1-incubator-SNAPSHOT</version>
    </dependency>
    <dependency>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-rt-frontend-jaxws</artifactId>
        <version>2.1-incubator-SNAPSHOT</version>
    </dependency>
    <dependency>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-rt-ws-security</artifactId>
        <version>2.1-incubator-SNAPSHOT</version>
    </dependency>
    ...
<dependencies>

Thanks for reading.

Advertisement

Web Service Versioning with Endpoints using XFire

I wanted to document an example of using web service versioning using endpoints. I posted on the XFire mailing list asking what other people do but got little in return. I read the XFire Versioning Wiki entry and that mentioned using namespaces and/or custom headers. Although the namespaces/custom header approach works fine, it has the significant drawback of your web service clients to actually modify the outgoing SOAP message, which is always nice to avoid.

Using endpoints also allows us to have nicer WSDL URLs based on version number, e.g.:

http://arsenalist.com/services/1_0_0/SportsService
http://arsenalist.com/services/1_0_1/SportsService

In this example, I want to host multiple versions of the service while maintaining a logical URL like shown above. There’s a simple little trick involved in doing this using XFire and it needs us to override the getService(HttpServletRequest) method of the XFireServletController class. In order to plug a different controller, we must override the createController() method in XFire’s main servlet. I’m using Spring so I would need to override XFireSpringServlet but if your’e not using Spring you would override XFireServlet instead.

public class CustomXFireServlet extends
                         XFireSpringServlet {
  public XFireServletController createController()
                      throws ServletException {
    return new CustomXFireServletController(getXFire(),
                                 getServletContext());
  }
}

So I’ve specified a new controller, CustomXFireServletController which is the primary entry point for all service requests. I’ll override the getService(HttpServletRequest) method which will parse the service name out of the URL as per your convention.

public class CustomXFireServletController extends
        XFireServletController {

  public CustomXFireServletController(XFire xFire) {
    super(xFire);
  }

  public CustomXFireServletController(XFire xFire,
                     ServletContext servletContext) {
    super(xFire, servletContext);
  }

  /**
   * Override getService to look for a URL of the form:
   * http://.../services/1_0_0/SportsService
   * which would map to a service registered as
   * SportsService1_0_0
   *
   * @param request HttpServletRequest
   * @return Service name corresponding to URL
   */
  protected String getService(HttpServletRequest
                                          request) {
    String pathInfo = request.getPathInfo();
    if (pathInfo != null && pathInfo.startsWith("/") &&
        StringUtils.countMatches(pathInfo, "/") == 2) {
      int lastSlash = pathInfo.lastIndexOf("/");
      String version = pathInfo.substring(1, lastSlash);
      String name = pathInfo.substring(lastSlash+1);
      return name + version;
    } else {
      return super.getService(request);
    }
  }
}

So I’m counting on a service with the name of SportsService1_0_0 to be registered with XFire. This service would be accessible at http://arsenalist.com/services/1_0_0/SportsService

The last step is to modify your web.xml so that it uses the CustomXFireServlet:

<servlet>
 <servlet-name>XFireServlet</servlet-name>
 <servlet-class>
  com.arsenalist.xfire.CustomXFireServlet
 </servlet-class>
</servlet>
<servlet-mapping>
 <servlet-name>XFireServlet</servlet-name>
 <url-pattern>/services/*</url-pattern>
</servlet-mapping>

This is one of the cleanest solutions for having versioning capabilities for your web services. Your web service clients don’t need to specify any specific namespaces or create custom headers just to invoke a specific version of a service. There are no external dependencies (not even on Spring) The endpoint method works on top of the SOAP envelope and you have the power to customize it to any convention that you might want to use.  For example, you could actually specify the version number using the querystring if you like:

http://arsenalist.com/services/SportsService?v=1.1

All these are delectable options that you can choose from.

Update: Andrew Ochsner’s excellent comment on this post also shows how you can do versioning using Spring’s DispatcherServlet and SimpleUrlHandlerMapping.