WS-Security demo

 

This guide will lead you through how to configure a client and service to use WS-Security. It assumes you've already got a basic client and server running. If you don't, please refer to the previous sections for how to set this up.

  • General set up
  • UsernameToken configuration
    • Server side
      • Mule Configuration
      • CXF Configuration
      • ServerPasswordCallback
    • Client configuration
      • Mule Configuration
      • CXF Configuration
      • Client password callback

General set up

The first thing you need to do is configure the Mule CXF connector to load up an external CXF configuration file. This configuration file is where we'll configure WS-Security.

<mule-configuration id="myConfiguration" version="1.0">
<connector name="cxf"
className="org.mule.providers.soap.cxf.CxfConnector">
<properties>
<property name="configurationLocation" value="my-cxf-config.xml" />
</properties>
</connector>

UsernameToken configuration

The UsernameToken feature in WS-Security is an interoperable way to exchange security tokens inside a SOAP message. In the following section we'll take a look at how to configure the client and server to exchange a username/password security token.

Server side

On the server side we need to:

  • Add a section in your my-cxf-config.xml file for the server
  • Configure the WSS4JInInterceptor and the SAAJInInterceptor. The former is responsible for checking the security of your message.
  • Write a Server PasswordCallback which verifies the password.

Mule Configuration

In the Mule configuration we need to configure our server. You should already be familiar with how to do this, but for completeness sake, here is the configuration for this example:

<mule-descriptor name="greeterService" 
implementation="org.apache.hello_world_soap_http.GreeterImpl"
singleton="true">
<inbound-router>
<endpoint address="cxf:http://localhost:63081/greeter">
<properties>
<property name="wsdlLocation" value="hello_world.wsdl" />
</properties>
</endpoint>
</inbound-router>
</mule-descriptor>

CXF Configuration

We need to write a CXF configuration file which contains our server configuration.

The <jaxws:server> element is what we use to configure the incoming interceptors on our service. The name attribute is very important. It is the QName of the WSDL port you wish to apply this configuration to. It is in the form your {service-namespace}local-port.

To determine your service namespace and port name go to your WSDL url (just append ?wsdl to your service address in the browser). The "targetNamespace" attribute on the <definitions> element is your service namespace. The <port> element near the bottom contains a "name" attribute which is your port name.

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:c="http://cxf.apache.org/core"
xmlns:jaxws="http://cxf.apache.org/jaxws"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">

<jaxws:server name="{http://apache.org/hello_world_soap_http}SoapPort" createdFromAPI="true">
<jaxws:inInterceptors>
<bean class="org.apache.cxf.binding.soap.saaj.SAAJInInterceptor" />
<bean class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">
<constructor-arg>
<map>
<entry key="action" value="UsernameToken" />
<entry key="passwordCallbackRef" value-ref="serverCallback"/>
</map>
</constructor-arg>
</bean>
</jaxws:inInterceptors>

</jaxws:server>

<bean id="serverCallback" class="org.mule.providers.soap.cxf.wssec.ServerPasswordCallback"/>
...
</beans>

Key things to note here:

  • We're installing the SAAJInInterceptor. The WSS4J implementation requires that we have a SAAJ tree in memory to work, so this is required.
  • We're installing teh WSS4JInInterceptor. We've configured it so it requires a UsernameToken. We've also told it about our ServerPasswordCallback which will verify the actual password.

ServerPasswordCallback

Our server callback simplify verifies the password. It does this by supplying the password which will be compared to the incoming password.

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.getIdentifer().equals("joe")) {
// set the password on the callback. This will be compared to the
// password which was sent from the client.
pc.setPassword("password");
}
}
}

Client configuration

On the client side, we need to:

  • Set up the CXF outbound endpoint
  • Configure the CXF client so that it uses ws-security
  • Set up a ClientPasswordCallback which supplies the password for the invocation

Mule Configuration

Here's a simple example which configures a CXF outbound endpoint:

<mule-descriptor 
name="cxfClient"
implementation="org.mule.providers.soap.cxf.jaxws.ClientMessageGenerator"
inboundEndpoint="quartz.in"
singleton="true">

<!-- An outbound endpoint which submits messages via a CXF client -->
<outbound-router>
<router className="org.mule.routing.outbound.OutboundPassThroughRouter">
<endpoint address="cxf:http://localhost:63081/greeter">
<properties>
<property name="clientClass" value="org.apache.hello_world_soap_http.SOAPService" />
<property name="port" value="SoapPort" />
<property name="wsdlLocation" value="/org/mule/providers/soap/cxf/wsa/hello_world.wsdl" />
<property name="operation" value="greetMe" />
</properties>
</endpoint>
</router>
</outbound-router>

</mule-descriptor>

CXF Configuration

We also need to add a configuration section to your "my-cxf-config.xml" file.

NOTE: if your client and your server are on separate machines, you will have two separate files, and then a CXF connector configuration on each one.

<jaxws:client name="{http://apache.org/hello_world_soap_http}SoapPort" createdFromAPI="true">
<jaxws:outInterceptors>
<bean class="org.apache.cxf.binding.soap.saaj.SAAJOutInterceptor" />
<bean class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor">
<constructor-arg>
<map>
<entry key="action" value="UsernameToken" />
<entry key="user" value="joe" />
<entry key="passwordType" value="PasswordDigest" />
<!-- The callback supplies the password so its not stored in our config file -->
<entry key="passwordCallbackRef" value-ref="clientCallback" />
</map>
</constructor-arg>
</bean>
</jaxws:outInterceptors>
</jaxws:client>

<bean id="clientCallback" class="org.mule.providers.soap.cxf.wssec.ClientPasswordCallback"/>

In this configuration snippet we're:

  • Telling CXF we wish to invoke the UsernameToken action.
  • Our username is "joe"
  • We want to send our password in digest form.
  • We should use the "clientCallback" bean to supply the password. (see below)

Client password callback

Here is an example client password callback. As you can see, it is fairly simple. It just sets the password you want to use for the outgoing invocation:

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("yourpassword");
}
}

你可能感兴趣的:(WS-Security demo)