WS-* Support - 6.3

Talend ESB Service Developer Guide

EnrichVersion
6.3
EnrichProdName
Talend Data Fabric
Talend Data Services Platform
Talend ESB
Talend MDM Platform
Talend Open Studio for ESB
Talend Real-Time Big Data Platform
task
Design and Development
Installation and Upgrade
EnrichPlatform
Talend ESB

WS-Addressing

WS-Addressing via XML Configuration / Java API

CXF provides support for the 2004-08 and 1.0 versions of WS-Addressing.

To enable WS-Addressing you may enable the WSAddressingFeature on your service. If you wish to use XML to configure this, you may use the following syntax:

<jaxws:endpoint id="{your.service.namespace}YourPortName">
   <jaxws:features>
      <wsa:addressing xmlns:wsa="http://cxf.apache.org/ws/addressing"/>
   </jaxws:features>
</jaxws:endpoint>

You can also use the same exact syntax with a <jaxws:client>

<jaxws: client id="{your.service.namespace}YourPortName">
   <jaxws:features>
      <wsa:addressing xmlns:wsa="http://cxf.apache.org/ws/addressing"/>
   </jaxws:features>
</jaxws:client>

From an API point of view this looks very similar:

import org.apache.cxf.jaxws.EndpointImpl;
import org.apache.cxf.ws.addressing.WSAddressingFeature;

MyServiceImpl implementor =  new MyServiceImpl()
EndpointImpl ep = (EndpointImpl) Endpoint.create(implementor);
ep.getFeatures().add(new WSAddressingFeature());
ep.publish("http://some/address");

You can also use it with the ClientProxyFactoryBeans and ServerFactoryBeans (and their JAX-WS versions, namely JaxWsProxyFactoryBean and JaxWsServerFactoryBean):

import org.apache.cxf.frontend.simple.ClientProxyFactoryBean;
import org.apache.cxf.ws.addressing.WSAddressingFeature;

ClientProxyFactoryBean factory = new ClientProxyFactoryBean();
factory.setServiceClass(MyService.class);
factory.setAddress("http://acme.come/some-service");
factory.getFeatures().add(new WSAddressingFeature());
MyService client = (MyService) factory.create();

Enabling WS-Addressing with WS-Policy

If you're using WS-Policy , CXF can automatically set up WS-Addressing for you if you use the <Addressing> policy expression.

Decoupled responses

By default, WS-Addressing using anonymous Reply-To addresses. This means the request/response patterns are synchronous in nature and the response will be sent back via the normal reply channel. However, WS-Addressing allows for a decoupled endpoint to be used for receiving the response with CXF then correlating it with the appropriate request. There are a few ways for configuring the address on which CXF will listen for decoupled WS-Addressing responses. For HTTP conduit configuration, its client configuration has an option for a DecoupledEndpoint address. If the conduit has this configured, all requests sent via that conduit will have WS-Addressing enabled and their responses sent to that endpoint:

<http:conduit 
   name="{http://apache.org/hello_world_soap_http}SoapPort.http-conduit"> 
   <http:client 
      DecoupledEndpoint="http://localhost:9090/decoupled_endpoint"/> 
</http:conduit>

The address can also be set via a Request Context property:

((BindingProvider)proxy).getRequestContext() 
   .put("org.apache.cxf.ws.addressing.replyto", 
   "http://localhost:9090/decoupled_endpoint");

The CXF AddressingPropertiesImpl object can be used to control many aspects of WS-Addressing including the Reply-To:

AddressingProperties maps = new AddressingPropertiesImpl(); 
EndpointReferenceType ref = new EndpointReferenceType(); 
AttributedURIType add = new AttributedURIType(); 
add.setValue("http://localhost:9090/decoupled_endpoint"); 
ref.setAddress(add); 
maps.setReplyTo(ref); 
maps.setFaultTo(ref); 

((BindingProvider)port).getRequestContext() 
   .put("javax.xml.ws.addressing.context", maps);

This method can also be used to configure the namespace (version) of the WS-Addressing headers, exact message ID's, and similar information.

WS-Discovery

WS-Discovery is a protocol to enable dynamic discovery of services available on the local network. By default, WS-Discovery uses a UDP based multicast transport to announce new services and probe for existing services. However, it also supports a managed mode where a discovery proxy is used to reduce the amount of multicast traffic required.

To enable CXF to send "Hello" announcements when services and endpoint are started, the cxf-services-ws-discovery-service and cxf-services-ws-discovery-api jars need to be available on the classpath. The cxf-services-ws-discovery-service jar will register a ServerLifecyleListener that will automatically publish the "Hello" messages. It will also respond to any Probe requests that match the services it has published.

By default, the WS-Discovery service will startup in ad-hoc mode. However, if you set a Bus property of "org.apache.cxf.service.ws-discovery.address" to a URL address of a WS-Discovery Proxy, the service will change to managed mode and send the Hello/Bye notices directly to that proxy. It will also not respond to Probes.

CXF also provides an API to probe the network or WS-Discovery proxy. The WSDiscoveryClient class (shown below) provides several methods for probing the network.

import org.apache.cxf.ws.discovery.WSDiscoveryClient;
...
// Use WS-Discovery to find references to services 
// that implement the Greeter portType
WSDiscoveryClient client = new WSDiscoveryClient();
   // or: new WSDiscoveryClient("soap.udp://proxyhost:3702");
List<EndpointReference> references = 
   client.probe(new QName("http://cxf.apache.org/hello_world/discovery", 
   "Greeter"));
client.close();
        
GreeterService service = new GreeterService();
//loop through all of them and have them greet me.
for (EndpointReference ref : references) {
   Greeter g = service.getPort(ref, Greeter.class);
   System.out.println(g.greetMe("World"));
}

WS-Policy

Developing Assertions

There are two steps involved in developing your domain specific assertions, these are:

  1. Implementing the Assertion and AssertionBuilder interfaces, and registering the AssertionBuilder with the AssertionBuilderRegistry

  2. Providing runtime support for the Assertion, either in form of an interceptor or inside a conduit or a destination, and registering that support if necessary.

The steps are outlined in more detail below:

Implementing the Assertion Interface

You can chose to implement the Assertion interface from scratch, or decide to use one of the existing Assertion implementations in the cxf-api module, extending them as required:

PrimitiveAssertion

This class represents an assertion without any attributes or child elements (in particular without a nested Policy element). The AnonymousResponses or NonAnonymousResponses assertions in the addressing metadata namespace http://www.w3.org/2007/01/addressing/metadata are examples of this type of assertion. The implementation of the equal and normalize methods in the class are trivial, and there should be no need to extend this class.

NestedPrimitiveAssertion

This class represents an assertion without any attributes, but with one mandatory nested Policy child element. The Addressing assertions in the addressing metadata namespace is an example of this type of assertion. The implementation of the equal and normalize methods are generic, and there should be no need to extend this class.

JaxbAssertion

This class represents an assertion described by an xml schema type that has been mapped to a Java class. The RM assertion as well as the assertions used in the HTTP module are extensions of this class. Although the equal and normalize methods are not abstract, you probably want to overwrite these methods.

Implementing and Registering the AssertionBuilder Interface

Assertion Builder class for custom assertion should implement AssertionBuilder<T> interface. The interface type can be Element, XMLStreamReader or OMElement.

Interface contains two methods: build() and getKnownElements().

Implementation of build() method should construct Assertion from the incoming type. It can be PrimitiveAssertion (without attributes or child elements), NestedPrimitiveAssertion (without attributes but with nested policy element) and JaxbAssertion (assertion described by any XML schema).

getKnownElements() method must return QNames of assertion elements from which assertion can be built.

Implementing the build method of the AssertionBuilder interface is straightforward (in the case of JaxbAssertions you can extend the JaxbAssertionBuilder class, which provides an appropriate JAXB context and some other useful methods).

The implementation of buildCompatible may need some more consideration if your assertion represents an element with attributes and/or child elements.

Registration of your AssertionBuilder with the AssertionBuilderRegistry is easy enough: simply add a bean for your AssertionBuilder to the cxf-* file of your module, or to the application's custom cfg file.

Implementing a Policy-Aware Interceptor

This is the easiest way of providing runtime support for an Assertion. Steps 1. and 2. listed in Interaction with the Framework can usually be coded as follows:

package mycompany.com.interceptors;
import org.apache.cxf.ws.policy.AssertionInfoMap;

class MyPolicyAwareInterceptor {
   static final QName assertionType = new QName("http://mycompany.com}", 
      "MyType"});
   public void handleMessage(Message message) {

      // get AssertionInfoMap
      org.apache.cxf.ws.policy.AssertionInfoMap aim = 
         message.get(org.apache.cxf.ws.policy.AssertionInfoMap.class);
      Collection<AssertionInfo ais> = aim.get(assertionType );

      // extract Assertion information
      for (AssertionInfo ai : ais) {
         org.apache.neethi.Assertion a = ai.getAssertion();
         MyAssertionType ma = (MyAssertionType)a;
         // digest ....
      }

      // process message ...
      // express support

      for (AssertionInfo ai : ais) {
         ai.setAsserted(...);
      }          
   }
}

Sometimes, it may be more convenient to spead the above functionality accross several interceptors, possibly according to chain (in, in fault, out, outfault). In any case, you need to also provide a PolicyInterceptorProvider, and declare a corresponding bean. Either implement one from scratch or use the PolicyInterceptorProviderImpl in the api package and customise it as follows (assuming that one and the same interceptor is used for all paths). The main task of policy interceptor provider is to say which interceptors must be activated for specified policy assertion:

<bean name="MyPolicyAwareInterceptor" 
   class="mycompany.com.interceptors.MyPolicyAwareInterceptor"/>
<bean class="org.apache.cxf.ws.policy.PolicyInterceptorProviderImpl">
   <constructor-arg>
      <!-- the list of assertion types supported 
            by this PolicyInterceptorProvider -->
      <list>
         <bean class="javax.xml.namespace.QName">
            <constructor-arg value="http://mycompany.com}"/>
            <constructor-arg value="MyType"/>
         </bean>
      </list>
   </constructor-arg>
   <property name="inInterceptors">
      <list>
         <ref bean="MyPolicyAwareInterceptor"/>
      </list>
   </property>
   <property name="inFaultInterceptors">
      <list>
         <ref bean="MyPolicyAwareInterceptor"/>
      </list>
   </property>
   <property name="outInterceptors">
      <list>
         <ref bean="MyPolicyAwareInterceptor"/>
   </list>
   </property>
   <property name="outFaultInterceptors">
      <list>
         <ref bean="MyPolicyAwareInterceptor"/>
      </list>
   </property>
</bean>

All beans of type PolicyInterceptorProvider are automatically registered with the framework's PolicyInterceptorProviderRegistry.

It is also possible to implement policy interceptor provider programmatically from scratch. It's constructor gives assertions QNames as argument of super constructor and adds corresponded interceptors using getters:

public class MyInterceptorProvider extends AbstractPolicyInterceptorProvider {
    private static final long serialVersionUID = -5248428637449096540L;
    private static final MyInInterceptor IN_INTERCEPTOR = new MyInInterceptor();
    private static final MyOutInterceptor OUT_INTERCEPTOR = new MyOutInterceptor();
    private static final MyOutFaultInterceptor OUT_FAULT_INTERCEPTOR = new MyOutFaultInterceptor();
     
    private static final Collection<QName> ASSERTION_TYPES;
    static {
        ASSERTION_TYPES = new ArrayList<QName>();
        ASSERTION_TYPES.add(new QName("www.mycompany.org", "myassertion"));
    }
 
    public MyInterceptorProvider() {
        super(ASSERTION_TYPES);
        getInInterceptors().add(IN_INTERCEPTOR);       
        getOutInterceptors().add(OUT_INTERCEPTOR);       
        getOutFaultInterceptors().add(OUT_FAULT_INTERCEPTOR);       
    }
}

Since version 2.5.2, Assertion builder and policy interceptor provider can be registered using CXF bus extension mechanism: just create a file META-INF/cxf/bus-extensions.txt containing the following:

org.company.MyInterceptorProvider::true
org.company.MyAssertionBuilder::true

Boolean value at the end specifies lazy loading strategy.

CXF automatically recognizes the assertion builder and policy interceptor provider and store them into registries: AssertionBuilderRegistry and PolicyInterceptorProviderRegistry.

Since CXF 2.6.0 it is possible to register multiple interceptor providers for single assertion.

Implementing a Policy-Aware Conduit/Destination
Initialization

Conduits/Destinations have access to the EndpointInfo object in their their constructors. Assuming they also have access to the bus, they can at any time in their lifecycle obtain the effective policy for the endpoint as follows:

class MyPolicyAwareConduit {
   static final QName assertionType = new QName("http://mycompany.com}", 
      "MyType"});

   void init() {
      PolicyEngine engine = bus.getExtenation(PolicyEngine.class);
      if (null != engine && engine.isEnabled()) {
      EffectiveEndpointPolicy ep = engine.getEndpointPolicy(endpoint, 
         this);
      Collection<Assertion> as = ep.getChosenAlternative();
      for (Assertion a : as) {
         if (assertType.equals(a.getName()) {
            // do something with it ...
         }
      }
      ... 
   }
   ...
}

and similarly for a Destination.

Policy-Aware Message Sending

Given access to the Message object, a conduit can, in its send method, proceed the same way as an interceptor in handleMessage. It can defer the updating of the assertion status in the AssertionInfo objects until called upon by the PolicyVerificationOutInterceptor, i.e. implement the status update in the assertMessage method. If the status update takes place inside of the send method itself, assertMessage, at least for outbound messages, can be implemented as a no-op.

Implementing the Assertor Interface

With canAssert, the conduit/destination simply informs the framework if it understands a given type of assertions. In assertMessage on the other hand, the conduit/destination expresses support (or the lack thereof) for specific assertion instances. See Verification for a description of how this API is used by the verifying policy interceptors in the POST_STREAM or PRE_INVOKE phases. HTTPConduit is an exmaple of a policy aware Conduit. It supports assertions of type HTTPClientPolicy, which are represented in the runtime as JaxbAssertion<HTTPClientPolicy> objects. HTTPConduit also has a data member of type HTTPClientPolicy. It implements assertMessage as follows: for outbound messages, it asserts all JaxbAssertion<HTTPClientPolicy> that are compatible with this data member. For inboun d messages, all HTTPClientPolicy assertions are asserted regardless their attributes. The rationale for this is that the sematics of the HTTPClientPolicy assertion effectively does not mandate any specific action on the inbound message. Similary, on its inbound path, the HTTPDestination asserts all HTTPServerPolicy assertions that are equal to the HTTPServerPolicy assertion configured for the destination, and all assertions of that type on the outbound path.

class MyPolicyAwareConduit implements Assertor {
   static final QName MYTYPE = new QName("http://mycompany.com}", 
      "MyType"});

   public boolean canAssert(QName name) {
      return MTYPE.equals(name);
   }

   public void assertMessage(Mesage message) {
      AssertionInfoMap = message.get(AssertioninfoMap.class);
      ...
   }
}

How It Works

Retrieval of Policies

Policies are associated with policy subjects. In the web services context, there are four different subjects:

  • Service

  • Endpoint

  • Operation

  • Message

Using WSDL 1.1, the policy-subject association usually takes the form of xml element attachment : A wsp:Policy element (the wsp prefix denotes the http://www.w3.org/2006/07/ws-policy namespace) is attached to a WSDL element such as wsdl:port. Alternatively, a wsp:PolicyReference elements is attached to a wsdl element. In that case, the actual wsp:Policy element can reside outside of the wsdl. Note that subjects do not correspond to wsdl elements directly. Rather, they map to a set of wsdl elements (see below). For example wsdl:port, wsdl:portType and wsdl:binding elements together describe the endpoint as a subject.

Another form of associating policies with policy subjects is external attachment : wsp:PolicyAttachment elements, which can reside in arbitrary locations, explicitly specify the subject(s) they apply to in their AppliesTo child element.

In CXF, elements attached to a wsdl element are available as extensors in the service model representation of that wsdl element. wsp:Policy or wsp:PolicyReference elements can be obtained as extensors of type UnknownExtensibilityElement in which the element name matches that of the wsp:Policy or wsp:PolicyReference element. Note that these attached elements are not parsed when the service model is built. With xml element attachment in WSDL 1.1, given a Message object, wsp:Policy elements attached to the endpoint or message subject can therefore be obtained by navigating the service model starting with the OperationInfo and/or EndpointInfo object stored in the message (or in the exchange).

The location of documents containing PolicyAttachment documents on the other hand needs to be made known to the framework. This can easily be achieved through configuration, see Specifying the Location of External Attachments .

PolicyAttachments are flexible w.r.t. the type of domain expressions. Domain expressions are used to identify entities such as endpoints, operations or messages with which a policy can be associated:

<wsp:PolicyAttachment>
   <wsp:AppliesTo> 
      <x:DomainExpression/> +
   </wsp:AppliesTo>
   (<wsp:Policy>...</wsp:Policy> |
       <wsp:PolicyReference>...</wsp:PolicyReference>)
</wsp:PolicyAttachment>

Currently, CXF supports only domain expressions of type wsa:EndpointReferenceType: They allow to associate the policies or policy references in an attachment with an endpoint (by means of matching the endpoint's address with that in the EndpointReferenceType element). It is not possible however to associate a Policy with an operation or a message this way. Support for other types of domain expressions can be plugged in by implementing the DomainExpressionBuilder interface and adding a corresponding bean to your configuration file (all DomainExpressionBuilder instances loaded that way will automatically register with the DomainExpressionBuilder and thus be considered in the process of parsing PolicyAttachment elements).

Once that the framework knows where to look for wsp:Policy elements, it can parses these elements and creates runtime presentations for them. This is where AssertionBuilders come into play: All child elements of a wsp:Policy element that are not in the wsp namespace are considered to be assertions. The framework will use its AssertionBuilderRegistry to find an AssertionBuilder registered for the element type in question and, if it finds one, proceed to build an Assertion object from that element (or else throw a PolicyException).

Computation of Effective Policies

As mentioned above, policies are associated with policy subjects. With WSDL 1.1, the effective policy for a subject is the aggregation, or the merge , of the policies attached to the wsdl elments representing that subject: The effective policy for a service subject is the merge of all policies applying to the wsdl:service element. The effective policy for an endpoint subject is the merge of all policies applying to the wsdl:port, wsdl:portType and wsdl:binding elements. The effective policy for an operation subject is the merge of all policies applying to the wsdl:portType/wsdl:operation and wsdl:binding/wsdl:operation elements. The effective policy for a (input | output | fault) message subject is the merge of all policies applying to the wsdl:message, (wsdl:portType/wsdl:operation/wsdl:input | wsdl:portType/wsdl:operation/wsdl:output | wsdl:portType/wsdl:operation/wsdl:fault) and (wsdl:binding/wsdl:operation/wsdl:input | wsdl:binding/wsdl:operation/wsdl:output | wsdl:binding/wsdl:operation/wsdl:fault).

Additional aggregation takes place to determine the effective policy of an endpoint: The effective policy for a service is the effective policy for the service subject. The effective policy for an endpoint is the merge of the effective policies for the service subject and the endpoint subject. The effective policy for an operation is the merge of the effective policies for the service subject, the endpoint subject and the operation subject. The effective policy for a (input | output | fault) message is the merge of the effective policies for the service subject, the endpoint subject, the operation subject and the message subject.

Multiple sources can be used to apply policies to the same subject. In the case of an endpoint subject for example, its associated wsdl:port element can have multiple wsp:Policy child elements. Also, a separate document can contain wsp:PolicyAttachment elements in which the AppliesTo children identify the endpoint in question as the target subject. Both the Policies attached to the port element as well as those in the matching PolicyAttachment elements will then contribute to the effective policy of the endpoint subject.

It is also important to keep in mind that the aggregation process described above makes it possible for an effective policy to have multiple assertion elements of the same type in one alternative (although this would not be considered the normal case). Different assertions of the same type within the same alternative do not overwrite each other. In fact, if used inappropriately, they may contradict each other. But it is also possible that they complement each other. Either way, the framework does not remove such duplicates and instead leaves it to the interceptors (or other Assertors) involved in the assertion process to decide if they can meaningfully deal with multiple assertions of the same type.

It is obvious that the above aggregation process can be quite resource intense. Effective policies for messages and endpoints are therefore cached by the framework for future reference. The entity that manages the cache of effective policies is the PolicyEngine.

When computing the effective policy for an endpoint or a message, the framework also chooses one of the effective policy's alternatives. Currently, it choses the first alternative in which all assertions may be supported, either by interceptors (i.e. there is a PolicyInterceptorProvider for the assertion type) or by the conduit/destination (if this implements the Assertor interface and through its canAssert method confirms that it can support the assertion type). However, even if such an alternative can be found, the chosen alternative is not necessarily supported: An interceptor may in principle be able to support a specific type of assertions, but it may not actually be able to support an individual instance of that assertion type.

The choice of alternative, along with the set of interceptors (obtained from the PolicyInterceptorProviders in the PolicyInterceptorProviderRegistry), is cached along with the actual effective message or endpoint policy in the form of an EffectivePolicy or EffectiveEndpointPolicy object. In the case of an effective endpoint policy, interceptors are chosen in such a way that the assertions in the chosen alternative of the effective endpoint policy can be supported, but also any assertion in any alternative of any of the operation and message specific policies. This is necessary in situations where the underlying message is not known, for example on the server inbound path: Once an alternative has been chosen for the effective policy of the server's endpoint we know which assertions must definitely be supported, regardless the underlying message/operation. Additional interceptors that are necessary to support the assertions that only appear in specific operation or input message policies are added pre-emptively. Note that this generally requires interceptors to be coded defensively - good practice anyway but especially so for interceptors returned by PolicyInterceptorProviders!

On-the-fly Provision of Interceptors

The policy framework, when activated (by loading the PolicyEngine and setting its "enabled" attribute to true), installs a couple of interceptors at bus level which execute early on in their respective interceptor chains:

Role

Chain

Phase

Interceptor

Effective Subject Policies Known

Client

Out

SETUP

ClientPolicy-OutInterceptor

Service, Endpoint, Operation, (Input) Message

Client

In

RECEIVE

ClientPolicy-InInterceptor

Service, Endpoint

Client

InFault

RECEIVE

ClientPolicy-InFault-Interceptor

Service, Endpoint

Server

In

RECEIVE

ServerPolicy-InInterceptor

Service, Endpoint

Server

OutFault

SETUP

ServerPolicy-OutFault-Interceptor

Service, Endpoint, Operation, (Fault) Message

Server

Out

SETUP

ServerPolicy-OutInterceptor

Service, Endpoint, Operation, (Out) Message

The main purpose of these policy interceptors is to add further interceptors that are required to support the effective policy of the underlying message - even if that policy is not yet known at the time the policy interceptor executes (because the operation is not yet known at that time). If the effective message policy is known, the assertions of its selected alternative are inserted into the message in the form of an AssertionInfoMap. This is a map, keyed by assertion type name, of collections of AssertionInfo objects, the latter being stateful (asserted/not asserted) representations of Assertion objects. When the effective message policy is not known, not only the assertions for the selected alternative in the effective endpoint policy are included in the AssertionInfoMap, but also all assertions in all alternatives of all of the operation and message specific policies. Not all of these will be asserted at the end of the chain, but that is fine if it turns out the unasserted assertions apply to operation sayHi when in fact the chain has been processing the message for a greetMe request!

Policy Aware Interceptors

Policy-aware interceptors extract the collection of AssertionInfo objects for the assertion types they understand from the AssertionInfoMap in the message. They can then use the wrapped Assertion objects to fine tune their behaviour, possibly exhibiting message specific behaviour. They can also express whether or not they could support these assertions. Given an assertion type that has attributes, and assuming there are two instances of assertions of this type, it is possible that the interceptor can assert one, but not the other. In any case, inability to support all assertions understood by the interceptor does not necessarily indicate a failure. As mentioned above in relation to pre-emptive interceptor installation, it is possible that the ones that cannot be supported do not in fact apply to the underlying message at all. Typically the interceptor would strive at supporting as many of these assertions as possible however, and to do so it may avail of the AssertionBuilder's capability to compute a compatible assertion. For example, by scheduling an acknowledgement to be sent in 3 seconds, an RM interceptor would support both of the following RMAssertions:

<wsrmp:RMAssertion 
   xmlns:wsrmp="http://schemas.xmlsoap.org/ws/2005/02/rm/policy">
   <wsrmp:AcknowledgementInterval Milliseconds="30000"/>
</wsrmp:RMAssertion>
<wsrmp:RMAssertion 
   xmlns:wsrmp="http://schemas.xmlsoap.org/ws/2005/02/rm/policy">
   <wsrmp:AcknowledgementInterval Milliseconds="50000"/>
</wsrmp:RMAssertion>
Verification

Another set of interceptors installed by the policy framework is responsible for verifying that one of the alternatives in the effective policy of the underlying message is indeed supported. These interceptors are:

Chain

Phase

Interceptor

Out, OutFault

POST_STREAM

PolicyVerificationOutInterceptor

In

PRE_INVOKE

PolicyVerificationInInterceptor

InFault

PRE_INVOKE

PolicyVerificationInFaultInterceptor

Their behaviour is symmetric on client and server side. On the outbound chain the effective message policy was known by the time the policy interceptor executing in the SETUP phase had inserted the AssertionInfoMap into the message. As the map was built exclusively from the Assertion objects that are part of the chosen alternative of the effective message policy, all of them must be supported. In other words, all of the AssertionInfo objects need to be in the asserted state. If one of them is not, the interceptor throws a Fault (wrapping a PolicyException).

On the inbound paths a little bit more work is necessary: If the message is a fault, we know by now what type of fault it is and what operation it applies to. If the message is not a fault message, knowing the underlying operation we can, from the location of the interceptor (client or server side), infer the message subject (input or output message). Either way, all information is now available to obtain the effective message policy. To check if any of is alternatives is supported, the policy verification interceptors then simply check if for each of its assertions the associated AssertionInfo object in the map is in the asserted state. If no alternative is supported, the interceptor throws a Fault (wrapping a PolicyException).

One thing worth noting is that - both on outbound and inbound chains - there may be assertions that only the conduit or destination can support. Although conduit or destination could access Assertion objects and tailor their behaviour when sending or receiving the current message, it is not knoan at this point whether this "tailoring" actually succeeded for the underlying message, i.e. whether the assertions in questions could actually be supported. For this reason, the policy verification interceptors check if the conduit or destination implements the Assertor interface. It it does, they pass it the Message object so they confirn their support (or the lack thereof) for these assertions. The above described traveral of the AssertionInfo map only takes place after the conduit or destination had a chance to m ake their contribution.

WS-Policy Framework Overview

The WS-Policy framework provides infrastructure and APIs that allow CXF users and developers to use WS-Policy.

It is compliant with the November 2006 draft publications of the Web Services Policy 1.5 - Framework and Web Services Policy 1.5 - Attachment specifications.

The framework consists of a core runtime and APIs that allow developers to plug in support for their own domain assertions:

Core

The core is responsible for:

  • retrieval of policies from different sources (wsdl documents, external documents)

  • computation of effective policies for service, endpoint, operation and message objects

  • on-the-fly provision of interceptors based on the effective policies for a particular message

  • verification that one of the effective policy's alternatives is indeed supported.

Policy operations such as merge and normalization (but not intersection) are based on Apache Neethi .

APIs
AssertionBuilder

The AssertionBuilder API is a concept from Neethi, slightly modified to avoid the dependency on the Axis object model, and extended to include support for domain specific behaviour of intersection and comparison.

public interface AssertionBuilder {
   // build an Assertion object from a given DOM element
   Assertion build(Element element);
   // return the schema type names of assertions understood by this builder
   Collection<QName> getSupportedTypes();
   // return an Assertion object that is 
   // compatible with the specified assertions
   Assertion buildCompatible(Assertion a, Assertion b);
}

AssertionBuilder implementations are loaded dynamically and are automatically registered with the AssertionBuilderRegistry, which is available as a Bus extension. Currently, CXF supports AssertionBuilder and Assertion implementations for the following assertion types:

{http://schemas.xmlsoap.org/ws/2005/02/rm/policy}RMAssertion
{http://www.w3.org/2007/01/addressing/metadata}Addressing
{http://www.w3.org/2007/01/addressing/metadata}AnonymousResponses
{http://www.w3.org/2007/01/addressing/metadata}NonAnonymousResponses
{http://cxf.apache.org/transports/http/configuration}client
{http://cxf.apache.org/transports/http/configuration}server

along with the WS-SecurityPolicy defined assertions.

They are all based on generic Assertion implementations (PrimitiveAssertion, NestedPrimitiveAssertion, JaxbAssertion) that developers can parameterize or extend when developing their own assertions, see Developing Assertions .

PolicyInterceptorProvider

This API is used to automatically engage interceptors required to support domain specific assertions at runtime, thus simplifying interceptor configuration a lot.

public interface PolicyInterceptorProvider extends InterceptorProvider {
   // return the schema types of the asssertions that can be supported
   Collection<QName> getAssertionTypes()
}

Currently, CXF supports PolicyInterceptorProvider implementations for the following assertion types:

{http://schemas.xmlsoap.org/ws/2005/02/rm/policy}RMAssertion
{http://www.w3.org/2007/01/addressing/metadata}Addressing
{http://www.w3.org/2007/01/addressing/metadata}AnonymousResponses
{http://www.w3.org/2007/01/addressing/metadata}NonAnonymousResponses

along with the WS-SecurityPolicy defined assertions.

In addition, the framework offers an API to refine domain expression(s) (xml elements describing policy subjects within a policy scope) in policy attachments. There is currently only one implementation for EndpointReferenceType domain expressions (matching over the address). Another implementation, using XPath expressions, is in work.

Interaction with the Framework

Components interact with the policy framework mainly in order to:

  1. retrieve the assertions pertaining to the underlying message (at least the ones known to the component) so the component can operate on the message accordingly

  2. confirm that the assertions pertaining to the underlying message are indeed supported.

Like most other CXF features, the policy framework is itself largely interceptor based. Thus, most interaction with the framework is indirect through the Message object: Policy interceptors make AssertionInfo objects (stateful representations of assertions) available to subsequently executing, policy-aware interceptors by inserting them into the Message object. Extracting the AssertionInfo objects from the Message allows interceptors to perform steps 1. and 2. above:

import org.apache.neethi.Assertion;

public class AssertionInfo {
   ...
   public boolean isAsserted() {...}
   public void setAsserted(boolean asserted) {...}
   public Assertion getAssertion() {...}
}

The WS-Addressing and WS-RM interceptors are examples for this style of intercation.

Somtimes, Conduits and destinations also want to assert their capabilities. But they cannot normally wait for Assertion information being made available to them via the Message object: Conduits may exhibit message specific behaviour (for example, apply message specific receive timeouts), but decisions made during the initialization phase may limit their capability to do so. And Destinations cannot normally exhibit message or operation specific behaviour at all. But both may still be able to support assertions in the effective endpoint's policy.

Their interaction with the policy framework therefore typically involves the PolicyEngine through which they obtain the effective policy for the underlying endpoint (for step 1.):

public interface PolicyEngine {
   ...
   EndpointPolicy getClientEndpointPolicy(EndpointInfo ei, 
      Conduit conduit);    
   EndpointPolicy getServerEndpointPolicy(EndpointInfo ei, 
      Destination destination); 
}

public interface EndpointPolicy {
   ...
   Policy getPolicy(); 
   Collection<Assertion> getChosenAlternative();
}

To perform step 2. they implement the Assertor interface (namely its assertMessage method):

public class Assertor {
   ...
   public boolean canAssert(QName name);
   public void assertMessage(Message message);
}

An example for policy aware conduits and destinations in CXF are the HTTP conduit and destination. They do support assertions of element type HTTPClientPolicy and HTTPServerPolicy respectively.

WS-ReliableMessaging

CXF supports the February 2005 version of the Web Services Reliable Messaging Protocol (WS-ReliableMessaging) specification. Like most other features in CXF, it is interceptor based. The WS-Reliable Messaging implementation consists of 4 interceptors in total:

Interceptor

Task

org.apache.cxf.ws.rm.RMOutInterceptor

Responsible for sending CreateSequence requests and waiting for their CreateSequenceResponse responses, and and aggregating the sequence properties (id and message number) for an application message.

org.apache.cxf.ws.rm.RMInInterceptor

Intercepting and processing RM protocol messages (these will not the application level), as well as SequenceAcknowledgments piggybacked on application messages.

org.apache.cxf.ws.rm.soap.RMSoapInterceptor

Encoding and decoding the RM headers

org.apache.cxf.ws.rm.soap.RetransmissionInterceptor

Responsible for creating copies of application messages for future resends.

Interceptor Based QOS

The presence of the RM interceptors on the respective interceptor chains alone will take care that RM protocol messages are exchanged when necessary. For example, upon intercepting the first application message on the outbound interceptor chain, the RMOutInterceptor will send a CreateSequence request and only proceed with processing the original application message after it has the CreateSequenceResponse response. Furthermore, the RM interceptors are responsible for adding the Sequence headers to the application messages and, on the destination side, extracting them from the message.

This means that no changes to application code are required to make the message exchange reliable!

You can still control sequence demarcation and other aspects of the reliable exchange through configuration however. For example, while CXF by default attempts to maximize the lifetime of a sequence, thus reducing the overhead incurred by the RM protocol messages, you can enforce the use of a separate sequence per application message by configuring the RM source's sequence termination policy (setting the maximum sequence length to 1). See the Reliable Messaging Configuration Guide for more details on configuring this and other aspects of the reliable exchange.

WS-SecureConversation

WS-SecureConversation support in CXF builds upon the WS-SecurityPolicy implementation to handle the SecureConversationToken policy assertions that could be found in the WS-SecurityPolicy fragment.

Note: Because the WS-SecureConversation support builds on the WS-SecurityPolicy support, this is currently only available to "wsdl first" projects.

One of the "problems" of WS-Security is that the use of strong encryption keys for all communication carries a hefty performance penalty on communication. WS-SecureConversation helps to alleviate that somewhat by allowing the client and service to use strong encryption at the start to negotiate a set of new security keys that will be used for furthur communication. This can be a huge benefit if the client needs to send many requests to the service. However, if the client only needs to send a single request and is then discarded, WS-SecureConversation is actually slower because the key negotiation requires an extra request/response to the server.

With WS-SecureConversation, there are two Security policies that come into effect:

  1. The "outer" policy that describes the security requirements for interacting with the actual endpoint. This will contain a SecureConversationToken in it someplace.

  2. The "bootstrap" policy that is contained in the SecureConverstationToken. This policy is the policy in affect when the client is negotiating the SecureConversation keys.

Configuring the WS-SecurityPolicy properties for WS-SecureConversation works exactly like the configuration for straight WS-SecurityPolicy. The only difference is that there needs to be a way to specify which properties are intended for the bootstrap policy in the SecureConversationToken and which are intended for the actual service policy. To accomplish this, properties intended for the SecureConversationToken bootstrap policy are appended with ".sct". For example:

<jaxws:client name="{http://InteropBaseAddress/interop} ...
   XDC-SEES_IPingService" createdFromAPI="true">
   <jaxws:properties>
      <!-- properties for the external policy -->
      <entry key="ws-security.username" value="abcd"/>

      <!-- properties for SecureConversationToken bootstrap policy -->
      <entry key="ws-security.username.sct" value="efgh"/>
      <entry key="ws-security.callback-handler.sct" 
         value="interop.client.KeystorePasswordCallback"/>
      <entry key="ws-security.encryption.properties.sct" 
         value="etc/bob.properties"/> 
   </jaxws:properties>
</jaxws:client>

Via the Java API, use code similar to the following:

org.apache.cxf.endpoint.Client client;
client.getRequestContext().put("ws-security.username.sct", username);
client.getRequestContext().put("ws-security.password.sct", password);

Via the Java API, use code similar to the following:

org.apache.cxf.endpoint.Client client;
client.getRequestContext().put("ws-security.username.sct", username);
client.getRequestContext().put("ws-security.password.sct", password);

Note: In most common cases of WS-SecureConversation, you won't need any configuration for the service policy. All of the "hard" stuff is used for the bootstrap policy and the service provides new keys for use by the service policy. This keeps the communication with the service itself as simple and efficient as possible.

WS-Security

WS-Security

WS-Security provides means to secure your services above and beyond transport level protocols such as HTTPS. Through a number of standards such as XML-Encryption, and headers defined in the WS-Security standard, it allows you to:

  • Pass authentication tokens between services

  • Encrypt messages or parts of messages

  • Sign messages

  • Timestamp messages

CXF relies on WSS4J in large part to implement WS-Security. Within your own services, WS-Security can be activated by using WS-SecurityPolicy, which provides a comprehensive and sophisticated validation of the security properties of a received message. A non-WS-SecurityPolicy approach is usually also possible by way of CXF interceptors added to your service and/or client as detailed in this section.

Overview of encryption and signing

WS-Security makes heavy use of public/private key cryptography. To really understand how to configure WS-Security, it is helpful - if not necessary - to understand these basics. The Wikipedia has an excellent entry on this, but we'll try to summarize the relevant basics here (This content is a modified version of the Wikipedia content.)

With public key cryptography, a user has a pair of public and private keys. These are generated using a large prime number and a key function.

The keys are related mathematically, but cannot be derived from one another. With these keys we can encrypt messages. For example, if Bob wants to send a message to Alice, he can encrypt a message using her public key. Alice can then decrypt this message using her private key. Only Alice can decrypt this message as she is the only one with the private key.

Messages can also be signed. This allows you to ensure the authenticity of the message. If Alice wants to send a message to Bob, and Bob wants to be sure that it is from Alice, Alice can sign the message using her private key. Bob can then verify that the message is from Alice by using her public key.

Configuring the WSS4J Interceptors

To enable WS-Security within CXF for a server or a client, you'll need to set up the WSS4J interceptors. You can either do this via the API for standalone web services or via Spring XML configuration for servlet-hosted ones. This section will provide an overview of how to do this, and the following sections will go into more detail about configuring the interceptors for specific security actions.

It is important to note that:

  1. If you are using CXF 2.0.x, you must add the SAAJ(In/Out)Interceptors if you're using WS-Security (This is done automatically for you from CXF 2.1 onwards). These enable creation of a DOM tree for each request/response. The support libraries for WS-Security require DOM trees.

  2. The web service provider may not need both in and out WS-Security interceptors. For instance, if you are just requiring signatures on incoming messages, the web service provider will just need an incoming WSS4J interceptor and only the SOAP client will need an outgoing one.

Adding the interceptors via the API

On the Server side, you'll want to add the interceptors to your CXF Endpoint. If you're publishing your service using the JAX-WS APIs, you can get your CXF endpoint like this:

import org.apache.cxf.endpoint.Endpoint;
import org.apache.cxf.jaxws.EndpointImpl;

EndpointImpl jaxWsEndpoint = 
   (EndpointImpl) Endpoint.publish("http://host/service", myServiceImpl);
Endpoint cxfEndpoint = jaxWsEndpoint.getServer().getEndpoint();

If you've used the (JaxWs)ServerFactoryBean, you can simply access it via the Server object:

import org.apache.cxf.endpoint.Endpoint;
import org.apache.cxf.endpoint.Server;
import org.apache.cxf.frontend.ServerFactoryBean;

ServerFactoryBean factory = ...;
...
Server server = factory.create();
Endpoint cxfEndpoint = server.getEndpoint();

On the client side, you can obtain a reference to the CXF endpoint using the ClientProxy helper:

GreeterService gs = new GreeterService();
Greeter greeter = gs.getGreeterPort();
...
org.apache.cxf.endpoint.Client client = 
   org.apache.cxf.frontend.ClientProxy.getClient(greeter);
org.apache.cxf.endpoint.Endpoint cxfEndpoint = client.getEndpoint();

Now you're ready to add the interceptors:

import org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor;
import org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor;
...

Map<String,Object> inProps= new HashMap<String,Object>();
... // how to configure the properties is outlined below;

WSS4JInInterceptor wssIn = new WSS4JInInterceptor(inProps);
cxfEndpoint.getInInterceptors().add(wssIn);

Map<String,Object> outProps = new HashMap<String,Object>();
... // how to configure the properties is outlined below;

WSS4JOutInterceptor wssOut = new WSS4JOutInterceptor(outProps);
cxfEndpoint.getOutInterceptors().add(wssOut);

Spring XML Configuration

If you're using Spring to build endpoints (e.g., web services running on a servlet container such as Tomcat), you can easily accomplish the above using your bean definitions instead.

<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath*:META-INF/cxf/cxf-extension-*.xml" />

<jaxws:endpoint id="myService"
   implementor="com.acme.MyServiceImpl"
   address="http://localhost:9001/MyService">

   <bean id="myPasswordCallback"
      class="com.mycompany.webservice.ServerPasswordCallback"/>

   <jaxws:inInterceptors>
      <bean class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">
         <constructor-arg>
            <map>
               <entry key="action" value="UsernameToken"/>
               <entry key="passwordType" value="PasswordDigest"/>
               <entry key="signaturePropFile" value="..."/>
               <entry key="passwordCallbackRef">
                  <ref bean="myPasswordCallback"/>
               </entry>
               ...
            </map>
         </constructor-arg>
      </bean>
   </jaxws:inInterceptors>
</jaxws:endpoint>

The entry keys and values given in the constructor-arg element above (action, signaturePropFile, etc.) map to the text strings in WSS4J's WSHandlerConstants and WSConstants classes for the corresponding WSHandlerConstants.XXXXX and WSConstants.XXXX constants you see in the section below. So by viewing WSHandlerConstants, for example, you can see that the WSHandlerConstants.USERNAME_TOKEN value given below would need to be "UsernameToken" instead when doing Spring configuration.

If you want to avoid looking up the text keys for the WSHandlerConstants.XXXXX and WSConstants.XXXX constants, you can also use the Spring util namespace to reference static constants in your Spring context as shown below.

<beans
   ...
   xmlns:util="http://www.springframework.org/schema/util"
   ...
   xsi:schemaLocation="
      ...
      http://www.springframework.org/schema/util 
      http://www.springframework.org/schema/util/spring-util.xsd">
   ...

   <bean class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">
      <constructor-arg>
         <map>
            <entry value="UsernameToken">
               <key>
                  <util:constant static-field=
                     "org.apache.ws.security.handler.WSHandlerConstants.ACTION"/>
               </key>
            </entry>
            ...
         </map>
      </constructor-arg>
   </bean>
   ...
</beans>
Additional Configuration Options

While the CXF WSS4J interceptors support the standard configuration properties available in WSHandlerConstants.XXXXX and WSConstants.XXXX, CXF also provides access to some additional low level configuration capabilities in WSS4J and some other security related interceptors.

Validating Signature and/or Encryption of Message Contents

As of CXF 2.2.8, the CryptoCoverageChecker interceptor allows one to validate signature and encryption coverage of message contents without migrating to a WS-SecurityPolicy based configuration. The interceptor can support enforcement of signature and encryption coverage at both the element and content level (be aware that the combination of signature and content do not represent a valid combination of coverage type and coverage scope). To configure this interceptor using the API, follow the example below.

import org.apache.cxf.ws.security.wss4j.CryptoCoverageChecker;
import org.apache.cxf.ws.security.wss4j.CryptoCoverageChecker.XPathExpression;
import org.apache.cxf.ws.security.wss4j.CryptoCoverageUtil.CoverageScope;
import org.apache.cxf.ws.security.wss4j.CryptoCoverageUtil.CoverageType;

Map<String, String> prefixes = new HashMap<String, String>();
   prefixes.put("ser", "http://www.sdj.pl");
   prefixes.put("soap", "http://schemas.xmlsoap.org/soap/envelope/");

List<XPathExpression> xpaths = Arrays.asList(
   new XPathExpression("//ser:Header", CoverageType.SIGNED, 
      CoverageScope.ELEMENT),
   new XPathExpression("//soap:Body", CoverageType.ENCRYPTED, 
      CoverageScope.CONTENT));

CryptoCoverageChecker checker = new CryptoCoverageChecker(prefixes, 
   xpaths);

The interceptor can also be configured in Spring using the conventional bean definition format.

After configuring the interceptor as above, simply add the interceptor to your client or server interceptor chain as shown previsouly with the WSS4J interceptors. Ensure that you include the WSS4JInInterceptor in the chain or all requests will be denied if you enforce any coverage XPaths.

The CryptoCoverageChecker is somewhat complex to set up for the most common use-cases for signature verification and decryption, as it involves adding XPath expressions and the corresponding prefix/namespace pairs. The DefaultCryptoCoverageChecker provides an easy way to ensure that the SOAP Body is signed or encrypted, that the Timestamp is signed, and that the WS-Addressing ReplyTo and FaultTo headers are signed (if they are present in the message payload).

The default configuation is that the SOAP Body, (WSU) Timestamp and WS-Addressing ReplyTo and FaultTo headers must be signed (if they exist in the message payload). This provides an out-of-the-box way of preventing XML Signature wrapping attacks. All that is required is that the DefaultCryptoCoverageChecker be added to the in-interceptor chain. For example:

<jaxws:inInterceptors> 
<bean class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor"> 
<constructor-arg> 
<map> 
<entry key="action" value="Signature Timestamp"/> 
<entry key="signaturePropFile" value="..."/> 
<entry key="passwordCallbackClass"value="..."/> 
</map> 
</constructor-arg> 
</bean> 
<bean class="org.apache.cxf.ws.security.wss4j.DefaultCryptoCoverageChecker"/> 
</jaxws:inInterceptors> 
Custom Processors

You can specify custom WSS4J Processor configuration on the WSS4JInInterceptor. To activate this configuration option, one provides a non-WSS4J defined property, wss4j.processor.map, to the WSS4JInInterceptor as shown in the following Spring example. The same configuration can be achieved through the API as well. The key value is an XML qualified name of the WS-Security header element to process with the given processor implementation. The entry values can be a String representing a class name of the processor to instantiate, an Object implementing Processor, or null to disable processing of the given WS-Security header element.

<bean class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">
   <constructor-arg>
      <map>
         ...
         <!-- This reconfigures the processor implementation that WSS4j 
               uses to process a WS-Security Signature element. -->
         <entry key="wss4j.processor.map">
            <map key-type="javax.xml.namespace.QName">
               <entry value="my.class">
                  <key>
                     <bean class="javax.xml.namespace.QName">
                        <constructor-arg 
                           value="http://www.w3.org/2000/09/xmldsig#"/>
                        <constructor-arg value="Signature"/>
                     </bean>
                  </key>
               </entry>
            </map>
         </entry>
         ...
      </map>
   </constructor-arg>
</bean>
Custom Actions

You can specify custom WSS4J Action configurations on the WSS4JOutInterceptor. To activate this configuration option, one provides a non-WSS4J defined property, wss4j.action.map, to the WSS4JOutInterceptor as shown in the following Spring example. The same configuration can be achieved through the API as well. The key value is an integer representing the WSS4J action identifier. The entry values can be a String representing a class name of the action to instantiate or an Object implementing Action. This configuration option allows you to override built-in action implementations or add your own.

<bean class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor">
   <constructor-arg>
      <map>
         ...
         <!-- Redefines the action for SAMLTokenSigned to use 
              a custom implementation.  -->
         <entry key="wss4j.action.map">
            <map key-type="java.lang.Integer" value-type="java.lang.Object">
               <entry key="0x10" value-ref="mySamlTokenSignedAction"/>
            </map>
         </entry>      
         ...
      </map>
  </constructor-arg>
</bean>

Configuring WS-Security Actions

Username Token Authentication

WS-Security supports many ways of specifying tokens. One of these is the UsernameToken header. It is a standard way to communicate a username and password or password digest to another endpoint. Be sure to review the OASIS UsernameToken Profile Specification for important security considerations when using UsernameTokens. If a nonce is present in a UsernameToken then it should be cached by the message recipient to guard against replay attacks. This behaviour is enabled by default starting with CXF 2.6.0. This functionality is also available from Apache CXF 2.4.7 and 2.5.3 onwards, but is not enabled by default at all for backwards-compatibility reasons. The following properties control nonce caching:

  • ws-security.enable.nonce.cache - The default value (for CXF 2.6.0) is "true" for message recipients, and "false" for message initiators. Set it to true to cache for both cases. The default value for CXF 2.4.x and 2.5.x is false.

  • ws-security.nonce.cache.instance - This holds a reference to a ReplayCache instance used to cache UsernameToken nonces. The default instance that is used is the EHCacheReplayCache, which uses Ehcache to cache the nonce values.

  • ws-security.cache.config.file - Set this property to point to a configuration file for the underlying caching implementation. By default the cxf-ehcache.xml file in the CXF rt-ws-security module is used.

For the server side, you'll want to set up the following properties on your WSS4JInInterceptor (see above for code sample):

inProps.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
// Password type : plain text
inProps.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);
// for hashed password use:
//properties.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_DIGEST);
// Callback used to retrieve password for given user.
inProps.put(WSHandlerConstants.PW_CALLBACK_CLASS, 
   ServerPasswordHandler.class.getName());

The password callback class allows you to retrieve the password for a given user so that WS-Security can determine if they're authorized. Here is a small example:

import java.io.IOException;
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 {

   public void handle(Callback[] callbacks) throws IOException, 
      UnsupportedCallbackException {

      WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];

      if (pc.getIdentifier().equals("joe")) {
         // set the password on the callback. 
         // This will be compared to the
         // password which was sent from the client.
         pc.setPassword("password");
      }
   }
}

Note that for up to and including CXF 2.3.x, the password validation of the special case of a plain-text password (or any other yet unknown password type) is delegated to the callback class, see org.apache.ws.security.processor.UsernameTokenProcessor#handleUsernameToken() method javadoc of the WSS4J project. In that case, the ServerPasswordCallback should be something like the following one:

public class ServerPasswordCallback implements CallbackHandler {

   public void handle(Callback[] callbacks) throws IOException, 
      UnsupportedCallbackException {

      WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];

      if (pc.getIdentifier().equals("joe") {
         if (!pc.getPassword().equals("password")) {
            throw new IOException("wrong password");
         }
      }
   }
}

For CXF 2.4 onwards, the callback handler supplies the password for all cases, and the validation is done internally (but can be configured). See here for more information. On the Client side you'll want to configure the WSS4J outgoing properties:

outProps.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
// Specify our username
outProps.put(WSHandlerConstants.USER, "joe");
// Password type : plain text
outProps.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);
// for hashed password use:
//properties.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_DIGEST);
// Callback used to retrieve password for given user.
outProps.put(WSHandlerConstants.PW_CALLBACK_CLASS, 
   ClientPasswordHandler.class.getName());

Once again we're using a password callback, except this time instead of specifying our password on the server side, we're specifying the password we want sent with the message. This is so we don't have to store our password in our configuration file.

import java.io.IOException;
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 {

   public void handle(Callback[] callbacks) throws IOException, 
      UnsupportedCallbackException {

      WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];

      // set the password for our message.
      pc.setPassword("password");
   }
}

In the case of multiple users with different passwords, use the getIdentifier() method of WSPasswordCallback to obtain the username of the current SOAP request.

Here is an example of WS-Security implemented using annotations for interceptors (uses UsernameToken).

Using X.509 Certificates

The X.509 Certificate Token Profile ( pdf ) provides another option for implementing WS-Security. For the Signature and Encryption actions, you'll need to create a public & private key for the entities involved. You can generate a self-signed key pair for your development environment via the following steps. Keep in mind these will not be signed by an external authority like Verisign, so are inappropriate for production use.

1. Creating private key with given alias and password like "myAlias"/"myAliasPassword" in keystore (protected by password for security reasons)

keytool -genkey -alias myAlias -keypass myAliasPassword -keystore \
privatestore.jks -storepass keyStorePassword -dname "cn=myAlias" -keyalg RSA

The alias is simply a way to identify the key pair. In this instance we are using the RSA algorithm.

2. Self-sign our certificate (in production environment this will be done by a company like Verisign).

keytool -selfcert -alias myAlias -keystore privatestore.jks -storepass 
keyStorePassword -keypass myAliasPassword

3. Export the public key from our private keystore to file named key.rsa

keytool -export -alias myAlias -file key.rsa -keystore privatestore.jks 
-storepass keyStorePassword

4. Import the public key to new keystore:

keytool -import -alias myAlias  -file key.rsa -keystore publicstore.jks 
-storepass keyStorePassword

So now we have two keystores containing our keys - a public one (publicstore.jks) and a private one (privatestore.jks). Both of them have keystore password set to keyStorePass (this not recommended for production but ok for development) and alias set to myAlias. The file key.rsa can removed from filesystem, since it used only temporarily. Storing keys in keystores is strongly advised because a keystore is protected by a password.

A more detailed description of key generation can be found here: http://java.sun.com/javase/6/docs/technotes/tools/solaris/keytool.html

How to create a production certificate can be found here: http://support.globalsign.net/en/objectsign/java.cfm

Signing

Signing a message is used to validate to the recipient that the message could only have come from a certain sender, and that the message was not altered in transit. It involves the sender encrypting a digest (hash) of the message with its private key, and the recipient decrypting the hash with the sender's public key, and recalculating the digest of the message to make sure the message was not altered in transit (i.e., that the digest values calculated by both the sender and recipient are the same). For this process to occur you must ensure that the Client's public key has been imported into the server's keystore using keytool.

On the client side, our outgoing WS-Security properties will look like so (see above for code sample):

outProps.put(WSHandlerConstants.ACTION, "Signature");
outProps.put(WSHandlerConstants.USER, "myAlias");
outProps.put(WSHandlerConstants.PW_CALLBACK_CLASS, 
   ClientCallbackHandler.class.getName());
outProps.put(WSHandlerConstants.SIG_PROP_FILE, "client_sign.properties");

The USER that is specified is the key alias for the client. The password callback class is responsible for providing that key's password.

Note

For X.509 support you will normally have multiple actions, e.g. Encryption with Signature. For these cases, just space-separate the actions in the ACTION property as follows:

outProps.put(WSHandlerConstants.ACTION, 
   WSHandlerConstants.TIMESTAMP + " " + 
   WSHandlerConstants.SIGNATURE + " " + 
   WSHandlerConstants.ENCRYPT);

Alternatively, you may space-separate the string literals you see above in the Spring configuration (e.g., "Signature Encrypt")

Our client_sign.properties file contains several settings to configure WSS4J:

org.apache.ws.security.crypto.merlin.keystore.type=jks
org.apache.ws.security.crypto.merlin.keystore.password=keyStorePassword
org.apache.ws.security.crypto.merlin.keystore.alias=myAlias
org.apache.ws.security.crypto.merlin.keystore.file=client_keystore.jks

On the server side, we need to configure our incoming WSS4J interceptor to verify the signature using the Client's public key.

inProps.put(WSHandlerConstants.ACTION, "Signature");
inProps.put(WSHandlerConstants.SIG_PROP_FILE, "server.properties");

Our server_sign.properties file contains several settings to configure WSS4J:

org.apache.ws.security.crypto.merlin.keystore.type=jks
org.apache.ws.security.crypto.merlin.keystore.password=amex123
org.apache.ws.security.crypto.merlin.keystore.file=server_keystore.jks
Encryption

Encryption involves the sender encrypting the message with the recipient's public key to ensure that only the recipient can read the message (only the recipient has its own private key, necessary for decrypting the message.) This requires the sender to have the recipient's public key in its keystore.

The process for encrypting is very similar to and indeed usually combined with the signature process above. Our WS-Security test sample provides an example of encrypting requests and responses.

Security Best Practices

Some guidelines are given at the WSS4J website about best practices when using WS-Security. It is recommended to study the guidelines carefully.

WS-SecurityPolicy

CXF 2.2 introduced support for using WS-SecurityPolicy to configure WSS4J instead of the custom configuration documented on the WS-Security page. However, all of the "background" material on the WS-Security page still applies and is important to know. WS-SecurityPolicy just provides an easier and more standards based way to configure and control the security requirements. With the security requirements documented in the WSDL as WS-Policy fragments, other tools such as .NET can easily know how to configure themselves to inter-operate with CXF services.

Enabling WS-SecurityPolicy

In CXF 2.2, if the cxf-rt-ws-policy and cxf-rt-ws-security modules are available on the classpath, the WS-SecurityPolicy stuff is automatically enabled. Since the entire security runtime is policy driven, the only requirement is that the policy engine and security policies be available.

If you are using the full "bundle" jar, all the security and policy stuff is already included.

Policy description

With WS-SecurityPolicy, the binding and/or operation in the wsdl references a WS-Policy fragment that describes the basic security requirements for interacting with that service. The WS-SecurityPolicy specification allows for specifying things like asymmetric/symmetric keys, using transports (https) for encryption, which parts/headers to encrypt or sign, whether to sign then encrypt or encrypt then sign, whether to include timestamps, whether to use derived keys, etc... Basically, it describes what actions are necessary to securely interact with the service described in the WSDL.

However, the WS-SecurityPolicy fragment does not include "everything" that is required for a runtime to be able to able to create the messages. It does not describe things such as locations of key stores, user names and passwords, etc... Those need to be configured in at runtime to augment the WS-SecurityPolicy fragment.

Configuring the extra properties

There are several extra properties that may need to be set to provide the additional bits of information to the runtime.

Table 1. User Properties

security.username

The user's name. It is used differently by each of the Security functions, see the JavaDoc for more information.

security.password

The user's password when security.callback-handler is not defined. It is currently only used for the case of adding a password to a UsernameToken. See the JavaDoc for more information.

security.signature.username

The user's name for signature. It is used as the alias name in the keystore to get the user's cert and private key for signature. See the JavaDoc for more information.

security.encryption.username

The user's name for encryption. It is used as the alias name in the keystore to get the user's public key for encryption. See the JavaDoc for more information.


Table 2. Callback Class and Crypto properties

security.callback-handler

The WSS4J security CallbackHandler class used for passwords.

security.saml-callback-handler

The SAML CallbackHandler class used to construct SAML Assertions.

security.signature.properties

The Crypto property configuration to use for signature, if "security.signature.crypto" is not set instead.

security.encryption.properties

The Crypto property configuration to use for encryption, if "security.encryption.crypto" is not set instead.

security.signature.crypto

A Crypto object to be used for signature. If this is not defined then "security.signature.properties" is used instead.

security.encryption.crypto

A Crypto object to be used for encryption. If this is not defined then "security.encryption.properties" is used instead.


Table 3. Boolean Security configuration tags (true or false values only)

Constant

Default

Description

security.validate.token

true

Whether to validate the password of a received UsernameToken or not.

security.enableRevocation

false

Whether to enable Certificate Revocation List (CRL) checking or not when verifying trust in a certificate.

security.username-token.always.encrypted

true

Whether to always encrypt UsernameTokens that are defined as a SupportingToken. This should not be set to false in a production environment, as it exposes the password (or the digest of the password) on the wire.

security.is-bsp-compliant

true

Whether to enforce compliance with the Basic Security Profile (BSP) 1.1 or not.

security.self-sign-saml-assertion

false

Whether to self-sign a SAML Assertion or not. If this is set to true, then an enveloped signature will be generated when the SAML Assertion is constructed.

security.enable.nonce.cache

(varies)

Whether to cache UsernameToken nonces. See See the JavaDoc for more information.

security.enable.timestamp.cache

(varies)

Whether to cache Timestamp Created Strings. See the JavaDoc for more information.


Table 4. Non-boolean Security Configuration parameters

security.timestamp. timeToLive

The time in seconds to append to the Creation value of an incoming Timestamp to determine whether to accept the Timestamp as valid or not. The default value is 300 seconds (5 minutes).

security.timestamp. futureTimeToLive

This configuration tag specifies the time in seconds in the future within which the Created time of an incoming Timestamp is valid. The default value is 60 seconds. See the JavaDoc for more information.

security. saml-role-attributename

The attribute URI of the SAML AttributeStatement where the role information is stored. The default is "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role".

security.kerberos.client

A reference to the Kerberos client object class used to obtain a service ticket.

security.spnego.client.action

The SpnegoClientAction implementation to use for SPNEGO. This allows the user to plug in a different implementation to obtain a service ticket.

security.kerberos.jaas.context

The JAAS Context name to use for Kerberos. This is currently only supported for SPNEGO.

security.kerberos.spn

The Kerberos Service Provider Name (spn) to use. This is currently only supported for SPNEGO.

security.nonce.cache.instance

This holds a reference to a ReplayCache instance used to cache UsernameToken nonces. The default instance that is used is the EHCacheReplayCache.

security.timestamp.cache.instance

This holds a reference to a ReplayCache instance used to cache Timestamp Created Strings. The default instance that is used is the EHCacheReplayCache.

security.cache.config.file

Set this property to point to a configuration file for the underlying caching implementation. The default configuration file that is used is cxf-ehcache.xml in the cxf-rt-ws-security module.

org.apache.cxf.ws.security. tokenstore.TokenStore

The TokenStore instance to use to cache security tokens. By default this uses the EHCacheTokenStore if EhCache is available. Otherwise it uses the MemoryTokenStore

security.subject.cert.constraints

A comma separated String of regular expressions which will be applied to the subject DN of the certificate used for signature validation, after trust verification of the certificate chain associated with the certificate. These constraints are not used when the certificate is contained in the keystore (direct trust).


Table 5. Validator implementations for validating received security tokens

security.ut.validator

The WSS4J Validator instance to use to validate UsernameTokens. The default value is the UsernameTokenValidator.

security.saml1.validator

The WSS4J Validator instance to use to validate SAML 1.1 Tokens. The default value is the SamlAssertionValidator.

security.saml2.validator

The WSS4J Validator instance to use to validate SAML 2.0 Tokens. The default value is the SamlAssertionValidator.

security.timestamp.validator

The WSS4J Validator instance to use to validate Timestamps. The default value is the TimestampValidator.

security.signature.validator

The WSS4J Validator instance to use to validate trust in credentials used in Signature verification. The default value is the SignatureTrustValidator.

security.bst.validator

The WSS4J Validator instance to use to validate BinarySecurityTokens. The default value is the NoOpValidator.

security.sct.validator

The WSS4J Validator instance to use to validate SecurityContextTokens. The default value is the NoOpValidator.


Table 6. STS Client Configuration tags

security.sts.client

A reference to the STSClient class used to communicate with the STS.

security.sts.applies-to

The "AppliesTo" address to send to the STS. The default is the endpoint address of the service provider.

security.sts.token.usecert

If true, writes out an X509Certificate structure in UseKey/KeyInfo. If false (the default), writes out a KeyValue structure instead.

security.sts.token.do.cancel

Whether to cancel a token when using SecureConversation after successful invocation. The default is "false".

security.cache.issued. token.in.endpoint

Set this to "false" to not cache a SecurityToken per proxy object in the IssuedTokenInterceptorProvider. This should be done if a token is being retrieved from an STS in an intermediary. The default value is "true".

security.sts.disable-wsmex-call-using-epr-address

Whether to avoid STS client trying send WS-MetadataExchange call using STS EPR WSA address when the endpoint contract contains no WS-MetadataExchange info. The default value is "false".

security.sts.token.crypto

A Crypto object to be used for the STS. See the JavaDoc for more information.

security.sts.token.properties

The Crypto property configuration to use for the STS. See the JavaDoc for more information.

security.sts.token.username

The alias name in the keystore to get the user's public key to send to the STS for the PublicKey KeyType case.

security.sts.token.act-as

The token to be sent to the STS in an "ActAs" field. See the JavaDoc for more information.

security.sts.token.on-behalf-of

The token to be sent to the STS in an "OnBehalfOf" field. See the JavaDoc for more information.


Note: for Symmetric bindings that specify a protection token, the security-encryption properties are used.

Configuring via Spring

The properties are easily configured as client or endpoint properties--use the former for the SOAP client, the latter for the web service provider.

<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">
 
   <jaxws:client name="{http://cxf.apache.org}MyPortName"
      createdFromAPI="true">
      <jaxws:properties>
         <entry key="security.callback-handler"
             value="interop.client.KeystorePasswordCallback"/>
         <entry key="security.signature.properties"
             value="etc/client.properties"/>
         <entry key="security.encryption.properties"
             value="etc/service.properties"/>
         <entry key="security.encryption.username"
             value="servicekeyalias"/>
      </jaxws:properties>
   </jaxws:client>
 
</beans>

For the jaxws:client's name attribute above, use the namespace of the WSDL along with the name attribute of the desired wsdl:port element under the WSDL's service section. (See here and here for an example.)

<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">
 
   <jaxws:endpoint
      id="MyService"
      address="https://localhost:9001/MyService"
      serviceName="interop:MyService"
      endpointName="interop:MyServiceEndpoint"
      implementor="com.foo.MyService">
 
      <jaxws:properties>
         <entry key="security.callback-handler"
             value="interop.client.UTPasswordCallback"/>
         <entry key="security.signature.properties"
             value="etc/keystore.properties"/>
         <entry key="security.encryption.properties"
             value="etc/truststore.properties"/>
         <entry key="security.encryption.username"
             value="useReqSigCert"/>
      </jaxws:properties>
 
   </jaxws:endpoint>
</beans>
Configuring via API's

Configuring the properties for the client just involves setting the properties in the client's RequestContext:

Map<String, Object> ctx = ((BindingProvider)port).getRequestContext();
ctx.put("security.encryption.properties", properties);
port.echoString("hello");

WS-Trust

WS-Trust support in CXF builds upon the WS-SecurityPolicy implementation to handle the IssuedToken policy assertions that could be found in the WS-SecurityPolicy fragment.

Note: Because the WS-IssuedToken support builds on the WS-SecurityPolicy support, this is currently only available to "wsdl first" projects.

WS-Trust extends the WS-Security specification to allow issuing, renewing, and validation of security tokens. A lot of what WS-Trust does centers around the use of a "Security Token Service", or STS. The STS is contacted to obtain security tokens that are used to create messages to talk to the services. The primary use of the STS is to acquire SAML tokens used to talk to the service. Why is this interesting?

When using "straight" WS-Security, the client and server need to have keys exchanged in advance. If the client and server are both in the same security domain, that isn't usually a problem, but for larger, complex applications spanning multiple domains, that can be a burden. Also, if multiple services require the same security credentials, updating all the services when those credentials change can by a major operation.

WS-Trust solves this by using security tokens that are obtained from a trusted Security Token Service. A client authenticates itself with the STS based on policies and requirements defined by the STS. The STS then provides a security token (example: a SAML token) that the client then uses to talk to the target service. The service can validate that token to make sure it really came from the trusted STS.

When the WS-SecurityPolicy runtime in CXF encounters an IssuedToken assertion in the policy, the runtime requires an instance of org.apache.cxf.ws.security.trust.STSClient to talk to the STS to obtain the required token. Since the STSClient is a WS-SecurityPolicy client, it will need configuration items to be able to create it's secure SOAP messages to talk to the STS.

General Configuration

There are several ways to configure the STSClient:

Direct configuration of an STSClient bean in the properties: In this scenario, a STSClient object is created directly as a property of the client object. The wsdlLocation, service/endpoint names, etc., are all configured in line for that client.

<jaxws:client name="{http://cxf.apache.org/}MyService" 
      createdFromAPI="true">
   <jaxws:properties>
      <entry key="ws-security.sts.client">
         <!-- direct STSClient config and creation -->
         <bean class="org.apache.cxf.ws.security.trust.STSClient">
            <constructor-arg ref="cxf"/>
            <property name="wsdlLocation" value="target/wsdl/trust.wsdl"/>
            <property name="serviceName" value=
               "{http://cxf.apache.org/securitytokenservice} ...
               SecurityTokenService"/>
            <property name="endpointName" value=
               "{http://cxf.apache.org/securitytokenservice} ...
               SecurityTokenEndpoint"/>
            <property name="properties">
               <map>
                 <entry key="ws-security.username" value="alice"/>
                 <entry key="ws-security.callback-handler" 
                    value="client.MyCallbackHandler"/>
                 <entry key="ws-security.signature.properties" 
                    value="clientKeystore.properties"/>
                 <entry key="ws-security.encryption.properties" 
                    value="clientKeystore.properties"/>
                 <entry key="ws-security.encryption.username" 
                    value="mystskey"/> 
               </map>
            </property>
         </bean>            
      </entry> 
   </jaxws:properties>
</jaxws:client>

The above example shows a configuration where the STS uses the UsernameToken profile to validate the client. It is assumed the keystore identified within clientKeystore.properties contains both the private key of the client and the public key (identified above as mystskey) of the STS; if not, create separate property files for the signature properties and the encryption properties, pointing to the keystore and truststore respectively.

Remember the jaxws:client createdFromAPI attribute needs to be set to true (as shown above) if you created the client programmatically via the CXF API's--i.e., Endpoint.publish() or Service.getPort().

This also works for "code first" cases as you can do:

STSClient sts = new STSClient(...);
sts.setXXXX(....)
.....
((BindingProvider)port).getRequestContext().
   put("ws-security.sts.client", sts);

Sample clientKeystore.properties format:

org.apache.ws.security.crypto.merlin.keystore.type=jks
org.apache.ws.security.crypto.merlin.keystore.password=KeystorePasswordHere
org.apache.ws.security.crypto.merlin.keystore.alias=ClientKeyAlias
org.apache.ws.security.crypto.merlin.keystore.file=NameOfKeystore.jks

Indirect configuration based on endpoint name: If the runtime does not find a STSClient bean configured directly on the client, it checks the configuration for a STSClient bean with the name of the endpoint appended with ".sts-client". For example, if the endpoint name for your client is "{http://cxf.apache.org/}TestEndpoint", then it can be configured as:

<bean name="{http://cxf.apache.org/}TestEndpoint.sts-client" 
   class="org.apache.cxf.ws.security.trust.STSClient" abstract="true">
   <property name="wsdlLocation" value="WSDL/wsdl/trust.wsdl"/>
   <property name="serviceName" value=
      "{http://cxf.apache.org/securitytokenservice} ...
      SecurityTokenService"/>
   <property name="endpointName" value=
      "{http://cxf.apache.org/securitytokenservice} ...
      SecurityTokenEndpoint"/>
   <property name="properties">
      <map>
         <entry key="ws-security.signature.properties" 
            value="etc/alice.properties"/> 
         <entry key="ws-security.encryption.properties" 
            value="etc/bob.properties"/>   
         <entry key="ws-security.encryption.username"
            value="stskeyname"/> 
      </map>
   </property>
</bean>

This properties configured in this example demonstrate STS validation of the client using the X.509 token profile. The abstract="true" setting for the bean defers creation of the STSClient object until it is actually needed. When that occurs, the CXF runtime will instantiate a new STSClient using the values configured for this bean.

Default configuration: If an STSClient is not found from the above methods, it then tries to find one configured like the indirect, but with the name "default.sts-client". This can be used to configure sts-clients for multiple services.

WS-Trust 1.4 Support

CXF supports some of the new functionality defined in the WS-Trust 1.4 specification. The currently supported features are listed below.

ActAs

The ActAs capability allows an initiator to request a security token that allows it to act as if it were somebody else. This capability becomes important in composite services where intermediate services make additional requests on-behalf of the true initiator. In this scenario, the relying party (the final destination of an indirect service request) may require information about the true origin of the request. The ActAs capability allows an intermediary to request a token that can convey this information.

The content of the ActAs element to be sent in the STS RequestSecurityToken call can be set in one of two ways:

  • By specifying a value for the JAX-WS property SecurityConstants.STS_TOKEN_ACT_AS ("ws-security.sts.token.act-as")

  • By specifying a value for the STSClient.actAs property.

For either case, the value can be one of the following:

  • A String

  • A DOM Element

  • A CallbackHandler object to use to obtain the token

For example, the following code fragment demonstrates how to use an interceptor to dynamically set the content of the ActAs element in the STS RST. Note that this interceptor is applied to the secured client, the initiator, and not to the STSClient's interceptor chain.

public class ActAsOutInterceptor extends 
   AbstractPhaseInterceptor<Message> {
   
   ActAsOutInterceptor () {
      // This can be in any stage before the WS-SP interceptors
      // setup the STS client and issued token interceptor.
      super(Phase.SETUP);
   }

   @Override
   public void handleMessage(Message message) throws Fault {
      message.put(SecurityConstants.STS_TOKEN_ACT_AS, ...);
   }
}

Alternatively, the ActAs content may be set directly on the STS as shown below.

<bean name="{http://cxf.apache.org/}TestEndpoint.sts-client" 
   class="org.apache.cxf.ws.security.trust.STSClient" abstract="true">
   <property name="wsdlLocation" value="WSDL/wsdl/trust.wsdl"/>
   <property name="serviceName" value=
      "{http://cxf.apache.org/securitytokenservice}SecurityTokenService"/>
   <property name="endpointName" value=
      "{http://cxf.apache.org/securitytokenservice}SecurityTokenEndpoint"/>
   <property name="actAs" value="..."/>
   <property name="properties">
      <map>
         ...
      </map>
   </property>
</bean>
OnBehalfOf

The OnBehalfOf capability allows an initiator to request a security token on behalf of somebody else. The content of the OnBehalfOf element to be sent in the STS RequestSecurityToken call can be set in one of two ways:

  • By specifying a value for the JAX-WS property SecurityConstants.STS_TOKEN_ON_BEHALF_OF ("ws-security.sts.token.on-behalf-of")

  • By specifying a value for the STSClient.onBehalfOf property.

The allowed values are the same as above for the ActAs property.