Building a SOAP Webservices Proxy Module using Spring Webservices

Some time ago I wanted to see how easy it is to write a web services proxy (wsproxy) using Spring Web Services. So, I thought I would share the result on Github. Feel free to use it (Apache v2 license) or let it serve as basis for your own development. The remainder of the article will explain the idea, how I used Spring web services to build it and a short guide on how to use the current implementation.

A wsproxy in this context is a central soap aware access layer which relays messages between systems. This relaying works in two directions; for services hosted internally (inbound mode) but also for services hosted externally were we are the client (outbound mode). One can compare this with a traditional http forward/reverse proxy but instead of operating on application transport it goes one level higher in the stack and deals with application messages; in this case soap. In outbound mode our internal services will (normally) use the wsproxy as a http forward proxy. It will then deal with delivering the received message to the actual target. For inbound mode the module will act as a reverse proxy accepting incoming messages from external clients and relay them to our internal services.

Before we continue, a few words about forward/reverse proxies:

A forward proxy is something the client explicitly configures in addition to specifying a target URL. The http stack of the client will send the message to the configured proxy and sent the host/port of the actual desired target host via http headers (Host header). The proxy then deals with forwarding the request to the desired target host/port and return the response to the client. Thus the proxy composes the host and port for the target URL out of the http Host header. For the path it will use the path from the request as received from the client. A reverse proxy behaves (from the point of the client) as the actual target server. The client is not aware of any proxies and does neither have to configure anything special. What the client uses as target URL is the URL of the reverse proxy. The reverse proxy will be able to intercept the message from the client and forward it to the actual target within the network. The reverse proxy will required extra configuration as for example the URL (host, port and possibly path) of the target service, as host/port cannot be deduced from the request or http headers.

When using this module, one can still implement an actual http reverse proxy on boundaries for TLS offloading or other transport purposes. For external inbound traffic (messages coming from external clients destined for internal service) the wsproxy module will simply be the second reverse proxy in line for incoming requests. For internal outbound traffic (messages from internal clients destined for external endpoints) the URL of the http reverse proxy will be configured as the target URL for that specific service on the wsproxy module.

One of the features of having such a soap aware wsproxy is centralizing concerns. The goal is that soap traffic passing through can be intercepted. This way we can implement things such as: audit logging, monitoring, access control, message security, … doing so we create a centralized place to deal with these requirements instead of having to re-implement them per application.

I chose Spring web services because there is no complicated infrastructure or design to understand. It it also very extensible and offers re-use at the right level for our requirements. However, at this time I should also point out that there are existing solutions out there which can do this as well and more. They often go under the name of xml security gateways and come as software package or fully equipped appliances. As always you should outweigh the benefits of these existing solutions over writing something yourself. Fact is that they don’t come for free (to say the least) and you still need someone with the right skills for configuring and maintaining them. As we will see, our requirements are easy to fulfill with a bit of code (and the help of Spring Web Services) giving us all the control we need.

For the requirements I had an easy to extend design in mind with following out of the box requirements:

  • Outbound mode needs to be configuration free for standard usages. This means that when needing to access a new external services our proxy should relay messages without requiring extra configuration
  • Messages passing the gateway need to be logged. Without extra configuration the entire message should be logged by default. Optionally, we need to be able to configure more fine grained which parts needs to be logged for specific services
  • For (mostly external) outbound communication we should be able to configure a forward proxy or override the target with a pre-configured host (an existing reverse proxy or the actual endpoint)
  • The module must be able to forward messages over secure transport in case there is no external reverse proxy present for offloading outbound secure transport. For inbound secure transport we will let this be handled by the container where the module is running on; so this is out of scope for the module
  • Be able to apply and handle message integrity/confidentiality

The component design looks like this:

There are 3 main components. The endpoint (catch-all endpoint in the drawing), which will act as the receiver for the message being sent to the wsproxy. The forwarder, which will relay the message to the target. Finally, the interceptor chains are the hooks where we are able to intercept the messages being sent/received and do something with them.

These 3 components are offered by Spring Web Services; the endpoint is a org.springframework.ws.server.endpoint.annotation.Endpoint implementing org.springframework.ws.server.endpoint.MessageEndpoint to be able to receive the raw payload. The forwarder uses org.springframework.ws.client.core.WebServiceTemplate and the interceptor chains are org.springframework.ws.client.support.interceptor.ClientInterceptor and/or org.springframework.ws.server.EndpointInterceptor depending on which sides they need to function (more on that later). For message security we will use WSS4J, but this is just an implementation of an interceptor, thus not a new component.

It is important to realize that there are two interceptor chains. From the point of the wsproxy, we’ll call the first one the “inbound chain”. This is the one operating between the client and the wsproxy. The “outbound chain” is the one operating between the wsproxy and target endpoint. So, if we have an internal client accessing an external endpoint via our wsproxy, the inbound chain will be invoked when the message is received by the wsproxy. The outbound chain will be invoked from the moment the wsproxy relays the message to the target endpoint. Spring has two interfaces to distinguish on which “side” the interceptor operates (an interceptor can also implement both interfaces, making it able to function on both sides). The org.springframework.ws.server.EndpointInterceptor operates on the endpoint side, for the wsproxy this is inbound. The org.springframework.ws.client.support.interceptor.ClientInterceptor operates on the client side, so for the wsproxy this is outbound. Btw; we’re using inbound and outbound rather then the original Spring naming (client/endpoint) to avoid confusion. As you’ve noticed by now, the wsproxy is also an endpoint and a client. However, when we refer to “client” we mean the actual service client and the “endpoint” is the actual target service.

The module itself will run on a standard JEE servlet container as your typical Spring application. For all inbound traffic the http (or https) connector from the container is used. For all outbound traffic the WebServiceTemplate is used configured with commons httpclient under the hood, which we’ll be able to use for both http and https if required. Service identification is done the “doclit” style. This means we take the first element of the body including it’s namespace. This is represented as a QName. This identification is important as we’ll have configuration on a per service basis, such as the forward proxy, forwarding protocol, endpoint URL mapping, specific loggers etc.

Ok enough of this. let’s take this baby for a spin! Import the project in your IDE of choice, make sure you import it as a Maven project as Maven will have to filter the file active_environment.properties (this is done automatically via the default profile). Then we will:

  • Setup a normal standalone soap based endpoint
  • Deploy wsproxy
  • Use a web services client to access the endpoint via the proxy module

For bootstrapping a simple endpoint there is a class SimpleEndpoint foreseen in the tests sources which uses the JDK internal JAX-WS and http server to bootstrap a webservice endpoint:

public class SimpleEndpoint {
	public static void main(String args[]) {
		Endpoint.publish("http://localhost:9999/simple", new SimpleWebServiceEndpoint());
		
		@WebService
		public static class SimpleWebServiceEndpoint {
			public Date getCurrentDate(String randomParameter) {
				return new Date();
				}
			}
		}
	}

Just run this as a new Java application, it will remain running until the process is killed. Boot the wsproxy by deploying the project to your server of choice (I’ll be using Tomcat7), there is no extra configuration required. As for the client we’ll be using soap-ui (you can also use cURL if you like). In soap-ui we first have to create the project. We do this based on the WSDL exposed by our test service (accessible under http://localhost:9999/simple?WSDL). Next, we’ll have to configure our wsproxy module as the http forward proxy in soap-ui:

The soap-ui projects are also availale in the project if you want. Don’t forget to enable the proxy settings as explained above, they are not saved as part of the project.

Important: don’t forget to disable the proxy settings again if you are starting with a new project. soap-ui will use the proxy settings for standard http traffic and not only for soap/http. For example; when creating a new project based on a WSDL URL, soap-ui will use the http proxy settings as well to retrieve the WSDL. Since the wsproxy module is not a pure http proxy (but a soap proxy instead) it will not allow non-soap traffic through.

The last thing we need to configure is the target URL in soap-ui. The wsproxy module is by default (on tomcat at least) deployed under the context root named after the filename. In our case this means the module is reachable under: http://localhost:8080/ws-proxy/ There are two options:

  • Deploy the module under the root of the application server (/) instead. In that case nothing needs to be changed to the target URL. The target URL will remain the same URL as you would be using without the proxy module
  • Use a context root of choice, but in that case you'll have to prefix the context root to the target URL

In our case we are in the second scenario, this means that we’ll have to change the proposed target URL from “http://localhost:9999/simple” to “http://localhost:9999/ws-proxy/simple”.

What happens is that soap-ui sends the request to the host/port specified in the proxy settings (it will thus not sent the request to localhost:9999 but to localhost:8080 instead). The path however is retained; the request is actually sent to localhost:8080 with path “ws-proxy/simple”. With the module deployed under “ws-proxy” you can now see why this path prefix has to be there. If the path would start with “simple” we would get a 404. The remainder of the path is not important for the infrastructure, as the Spring dispatcher servlet (configuration can be found in WsProxyWebApplicationInitializer) is bound to “/*”. So every subsequent path is treated by the servlet in every case.

To be able to forward the message to the actual target, the module will calculate the target URL:

  • First check if there is a pre-configured target URL for the given endpoint based upon the service identification (payload root element + namespace). This is configured in EndpointTargetUrlMapping as we will see later.
  • If nothing found, check if there is a http Host header present, use host:port as the target server. For the path, use the path as present in the request, but subtract the context root under which this module is deployed (if any)

The latter means that in our case the module is deployed under “ws-proxy” and the request path is “ws-proxy/simple”, which will result in a target URL of “http://localhost:999/simple” When executing the request, we’ll get this answer:

In the wsproxy log file we can see the intercepted request and response being logged:

51948 [http-bio-8080-exec-5] DEBUG be.error.wsproxy.interceptors.internalchain.LoggingInterceptor  - SID:{http://wsproxy.error.be/}getCurrentDate INBOUND SIDE Request:

   
   
      
         
         ?
      
   

 
51949 [http-bio-8080-exec-5] DEBUG be.error.wsproxy.core.ForwardingClient  - Using information from Host header as hostname/port
51949 [http-bio-8080-exec-5] DEBUG be.error.wsproxy.core.ForwardingClient  - Got webservice forwarding request, sending to:http://localhost:9999/simple
51981 [http-bio-8080-exec-5] DEBUG be.error.wsproxy.core.ForwardingClient  - Using interceptors:[class be.error.wsproxy.interceptors.externalchain.HttpRequestHeaderTransfererInterceptor]
51981 [http-bio-8080-exec-5] DEBUG be.error.wsproxy.core.ForwardingClient$3  - Opening [org.springframework.ws.transport.http.HttpComponentsConnection@1dd5e19a] to [http://localhost:9999/simple]
51991 [http-bio-8080-exec-5] DEBUG be.error.wsproxy.core.ForwardingClient  - Forwarding (http://localhost:9999/simple) done.
51994 [http-bio-8080-exec-5] DEBUG be.error.wsproxy.interceptors.internalchain.LoggingInterceptor  - SID:{http://wsproxy.error.be/}getCurrentDate INBOUND SIDE Response:

  
    
      2013-10-28T15:55:29.717+01:00
    
  

In the default setup default logging happens on the inbound side. The inbound interceptors are configure here:

@Configuration
public class InboundInterceptors {
 
 @Autowired
 private PayloadRootAnnotationMethodEndpointMapping catchAllEndpointMapping;
 @Autowired
 private MessageDispatcher messageDispatcher;
 
 @Configuration
 public static class FirstInlineInterceptors {
  @Bean
  public DelegatingSmartSoapEndpointInterceptor loggingInterceptor() {
   return new DelegatingSmartSoapEndpointInterceptor(new LoggingInterceptor());
  }
 }
 
 @Configuration
 public static class ServiceSpecificInterceptors {
 
 }
 
 @Configuration
 public static class LastInLineInterceptors {
 
 }
}
 

If you want to configure this logging interceptor also on the outbound side, you can add them in OutboundInterceptors. LoggingInterceptor both implements EndpointInterceptor and ClientInterceptor. To continue with our requirements, there is also an interceptor which is able to log fragments based on XPath expression. The LoggingXPathInterceptor is service specific and hence we’ll be adding this one to the ServiceSpecificInterceptors. The difference is that service specific interceptors use PayloadRootSmartSoapEndpointInterceptor which we need to give namespace and payload root element to identify the service. The configured interceptor will only be invoked for that service. The first in line and last in line use DelegatingSmartSoapEndpointInterceptor which will be invoked for any request.

@Configuration
public static class ServiceSpepcificInterceptors {
 @Bean
 public PayloadRootSmartSoapEndpointInterceptor getCurrentDateLoggingInterecptor() {
  LoggingXPathInterceptor loggingXPathInterceptor = new LoggingXPathInterceptor();
  loggingXPathInterceptor.addRequestXPaths(new WebServiceMessageXPathExpressionMetaData(
    "//*[local-name()='arg0']", "requestParameter"));
  loggingXPathInterceptor.addResponseXPaths(new WebServiceMessageXPathExpressionMetaData(
    "//*[local-name()='return']", "responseParameter"));
  return new PayloadRootSmartSoapEndpointInterceptor(loggingXPathInterceptor, "http://wsproxy.error.be/",
    "getCurrentDate");
 }
}
 

When we execute the request again in soap-ui, we can see that the request argument and response value are extracted logged to our log file:

DEBUG be.error.wsproxy.interceptors.internalchain.LoggingXPathInterceptor - SID:{http://wsproxy.error.be/}getCurrentDate XPATHID:requestParameter VALUE:?
DEBUG be.error.wsproxy.interceptors.internalchain.LoggingXPathInterceptor - SID:{http://wsproxy.error.be/}getCurrentDate XPATHID:responseParameter VALUE:2013-10-28T16:50:29.537+01:00

The WebServiceMessageXPathExpressionMetaData operates by default on the soap body (payload) and treats the given XPaths as mandatory (but non blocking). To see other options check the javadoc on WebServiceMessageXPathExpressionMetaData.

The properties that can be configured are located in the package be.error.wsproxy.configuration.properties. Following classes exist: <ul><li>EndpointProtocolMapping</li><li>EndpointTargetUrlMapping</li><li>ForwardProxy</li><li>Keystores</li></ul> The default Spring profile “local”, enabled via Maven the default Maven filter, will resolve them from the properties file in wsproxy_local_demo.properties. The configuration is always stored as a simple string allowing for easy externalization in for example a JNDI environment. The three first properties determine how messages will be forwarded, to start with EndpointProtocolMapping:

In the above scenario the target URL was automatically deduced from the Host parameters as our internal client was using the module as a forward proxy. Since the host parameters do not contain any notion about protocols, the wsproxy assumed http as the forwarding protocol by default. If don’t have a reverse proxy which takes care of offloading TLS, you can ask the proxy module to forward over https instead. You can do this by setting the protocol mapping to https for a specific service:

endpoint.protocol.mapping={namespace}payloadRootElementLocalName=https,...

EndpointTargetUrlMapping allows to define the target URL directly. This is required in the scenario where an external client will be accessing our internal service. In that case the target URL can no longer be deduced; external clients will not use our module as forward proxy but the message will simply end up on our module as it would be the actual service. The module then needs to know to where it should forward the message to:

endpoint.target.url.mapping={namespace}payloadRootElementLocalName=http(s)://host:port/path,....

This can also be used to override the target URL all together. The forwarder will first look if there is an explicit URL defined for the given service, if so that one will be given precedence.

The ForwardProxy can be configured when the wsproxy module on its turn needs to communicate via a http forward proxy in order to reach the target. This is also configurable on a per service basis.

Remember that the forward proxy does not change anything on how the target URL is calculated. Instead of directly accessing the target URL, the message will be forward to the configurered proxy if the setting is used:

forward.proxy=={namespace}payloadRootElementLocalName=host:port,... 

The keystores point to the keystore configuration containing store location, store password, key alias and key password. They are used when we want to apply message security which we’ll cover next.

keystores.location=${project.root.dir}/config/test-keystores
keystore=${keystores.location}/keystore.jks
keystore.password=changeme
key.alias=mykey
key.password=changeme
truststore=${keystores.location}/truststore.jks
truststore.password=changeme

To satisfy the last requirement (integrity/confidentiality) we’ll be using WSS4J via the Spring Wss4jSecurityInterceptor. This interceptor needs to be configured on the outbound side in our example were we have an internal client accessing an external service. The steps we will perform:

  • Setup a secured standalone soap based endpoint
  • Configure the wsproxy with message security for the given service
  • Deploy wsproxy
  • Use a web services client to access the endpoint via the proxy module

For the secured endpoint SimpleSecuredEndpoint is foreseen using JAXWS and WSIT. The WSIT configuration can be found in META-INF/wsit-be.error.wsproxy.SimpleSecuredEndpoint$SimpleWebServiceEndpoint.xml enabling message integrity on our endpoint.

public class SimpleSecuredEndpoint {
 
 public static void main(String args[]) throws IOException {
  // Set WSIT_HOME manually, we're only using this for testing purposes. This way we can have a dynamic path based
  // on the project location in filesystem to resolve the keystores via the WSIT configuratin in META-INF
  System.setProperty("WSIT_HOME", new ClassPathResource("").getFile().getParent() + "/../config/test-keystores/");
  Endpoint.publish("http://localhost:9999/simple", new SimpleWebServiceEndpoint());
 }
 
 @WebService(serviceName = "SimpleEndpoint")
 @Addressing(enabled = false, required = false)
 public static class SimpleWebServiceEndpoint {
  public Date getCurrentDateSecured(String randomParameter) {
   return new Date();
  }
 }
}

Important: the JAXWS implementation shipped with the JDK does not contain WSIT. It is just the JAXWS RI. In order for this to work you’ll have to download the latest Metro release yourself which bundles everything together. See Metro home page. When you’ve downloaded Metro, run SimpleSecuredEndpoint passing along the endorsed system property: -Djava.endorsed.dirs=/path_to_metro/lib. This will ensure the entire JAXWS implementation is used from the external libraries. When everything is running fine you’ll be seeing a line: INFO: WSP5018: Loaded WSIT configuration from file: file:/home/koen/…..

The configuration of the WSS4J interceptor enabling message integrity in OutboundInterceptors:

@Bean
 public Map<QName, List<ClientInterceptor>> customClientInterceptors() throws Exception {
  Map<QName, List<ClientInterceptor>> mapping = new HashMap<>();
 
  List<ClientInterceptor> list = new ArrayList<>();
  list.add(getCurrentDateServiceSecurityInterceptor());
  list.add(new LoggingInterceptor());
  mapping.put(new QName("http://wsproxy.error.be/", "getCurrentDateSecured"), list);
 
  return mapping;
 }
 
 private Wss4jSecurityInterceptor getCurrentDateServiceSecurityInterceptor() throws Exception {
  Wss4jSecurityInterceptor interceptor = new Wss4jSecurityInterceptor();
 
  // Outgoing
  interceptor.setSecurementActions("Signature Timestamp");
  interceptor
    .setSecurementSignatureParts("{}{http://schemas.xmlsoap.org/soap/envelope/}Body;{}{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd}Timestamp");
  interceptor.setSecurementSignatureKeyIdentifier("IssuerSerial");
  Pair<String, String> key = keystore.getKeyAliasPasswords().get(0);
  interceptor.setSecurementUsername(key.getLeft());
  interceptor.setSecurementPassword(key.getRight());
  interceptor.setSecurementSignatureAlgorithm("http://www.w3.org/2000/09/xmldsig#rsa-sha1");
  interceptor.setSecurementSignatureDigestAlgorithm("http://www.w3.org/2000/09/xmldsig#sha1");
  interceptor.setSecurementTimeToLive(700);
  interceptor.setValidationTimeToLive(700);
  interceptor.setSecurementSignatureCrypto(keystoreCrypto);
 
  // Incomming
  interceptor.setValidationActions("Timestamp Signature");
  interceptor.setValidationSignatureCrypto(truststoreCrypto);
 
  return interceptor;
 }

On line 6 and 7 we add the custom interceptor to the list of interceptors used by the ForwardingClient. We’ve also added the LoggingInterceptor on the outbound so we can see the secured messages going out and coming in. To test the message security configuration deploy the wsproxy and use soap-ui to fire the request. The soap-ui setup is no different then setup for the non-secured endpoint.

Important:There seems to be an issue with C14N. When the request is sent as normal, WSIT will complain that the calculated digest does not match with the one in the message. I’m going to investigate this further, but this appears to be a problem of WSIT and not WSS4J, since the same problem also happens when soap-ui is configured as a secured client and directly communicates with the endpoint rather then using the wsproxy module. To get around this and see the test working, remove the line feed between the soap Body start element and the payload root start element. Also remove the line feed between the soap Body end element and the payload root end element:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsp="http://wsproxy.error.be/">
   <soapenv:Header/>
   <soapenv:Body><wsp:getCurrentDateSecured>²
         <!--Optional:-->
         <arg0&lgt;?</arg0>
 </wsp:getCurrentDateSecured<</soapenv:Body>
</soapenv:Envelope>

The soap-ui projects are also availale in the project if you want. Don’t forget to enable the proxy settings as explained before, they are not saved as part of the project. The result:

  <S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:exc14n="http://www.w3.org/2001/10/xml-exc-c14n#" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:xs="http://www.w3.org/2001/XMLSchema">
   <S:Header/&gt²;
   <S:Body wsu:Id="_5002"&gt²;
      <ns2:getCurrentDateSecuredResponse xmlns:ns2="http://wsproxy.error.be/"&gt²;
         <return<2013-10-29T14:26:25.789+01:00>/return&gt²;
      </ns2:getCurrentDateSecuredResponse>
   </S:Body>
</S:Envelope>
  

Nothing spectacular as the wsproxy added message security when forwarding the request and removed it when returning the response. If we look at the wsproxy log files, we’ll first see the request entering on the inboud side:

  34   [http-bio-8080-exec-3] DEBUG be.error.wsproxy.interceptors.logging.LoggingInterceptor  - SID:{http://wsproxy.error.be/}getCurrentDate INBOUND SIDE Request:<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsp="http://wsproxy.error.be/">
   <soapenv:Header/>
   <soapenv:Body>
    <wsp:getCurrentDateSecured>
         <!--Optional:-->
         <arg0>?>/arg0>
 </wsp:getCurrentDateSecured>
  </soapenv:Body>
</soapenv:Envelope>
 

The request is secured and forward to the endpoint:

  394  [http-bio-8080-exec-3] DEBUG be.error.wsproxy.interceptors.logging.LoggingInterceptor  - SID:{http://wsproxy.error.be/}getCurrentDate OUTBOUND SIDE Request:<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsp="http://wsproxy.error.be/">
   <soapenv:Header>
    <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" soapenv:mustUnderstand="1">
      <wsu:Timestamp wsu:Id="TS-518848887F924441AB13830540361321">
        <wsu:Created>2013-10-29T13:40:36.130Z</wsu:Created>
        <wsu:Expires>2013-10-29T13:45:36.130Z</wsu:Expires>
      </wsu:Timestamp>
      <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Id="SIG-518848887F924441AB13830540361916">
        <ds:SignedInfo>
...
 

The secured response is received from the endpoint;

 
  524  [http-bio-8080-exec-3] DEBUG be.error.wsproxy.interceptors.logging.LoggingInterceptor  - SID:{http://wsproxy.error.be/}getCurrentDate OUTBOUND SIDE Response:<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:exc14n="http://www.w3.org/2001/10/xml-exc-c14n#" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <S:Header>
    <wsse:Security S:mustUnderstand="1">
      <wsu:Timestamp xmlns:ns15="http://www.w3.org/2003/05/soap-envelope" xmlns:ns16="http://docs.oasis-open.org/ws-sx/ws-secureconversation/200512" wsu:Id="_3">
        <wsu:Created>2013-10-29T13:40:36Z</wsu:Created>
        <wsu:Expires>2013-10-29T13:45:36Z</wsu:Expires>
      </wsu:Timestamp>
      <ds:Signature xmlns:ns15="http://www.w3.org/2003/05/soap-envelope" xmlns:ns16="http://docs.oasis-open.org/ws-sx/ws-secureconversation/200512" Id="_1">
        <ds:SignedInfo>
...
 

Security information is processed and validated. If ok, the security information is stripped and the response returned (to our client, soap-ui in this case):

 567  [http-bio-8080-exec-3] DEBUG be.error.wsproxy.interceptors.logging.LoggingInterceptor  - SID:{http://wsproxy.error.be/}getCurrentDate INBOUND SIDE Response:<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:exc14n="http://www.w3.org/2001/10/xml-exc-c14n#" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <S:Header/>
  <S:Body wsu:Id="_5002">
    <ns2:getCurrentDateSecuredResponse xmlns:ns2="http://wsproxy.error.be/">
      <return>2013-10-29T14:40:36.357+01:00</return>
    </ns2:getCurrentDateSecuredResponse>
  </S:Body>
</S:Envelope>
 

Until now all tests have been done simulating an internal client accessing an external service. In case you want to use the module to do the inverse; serving external clients accessing internally hosted services, it is more of the same. For each internally hosted service to which the module will forward you would have to register a target URL using the endpoint.target.url.mapping configuration parameter. Interceptors continue working the same way, but remember that for example for message security you probably want the Wss4jSecurityInterceptor to be configured on the inbound side, as in this scenario the inbound side is the external facing side. There is no issue in configuring the Wss4jSecurityInterceptor on both inbound and outbound side for different service; all configuration is based on a per service basis.

For example: service x (identified by namespace and payload root element) is an internal hosted service. Service y is an external service which internal clients want to access. For securing our internal service x we would be adding Wss4jSecurityInterceptor as service specific inbound interceptor in the InboundInterceptors configuration. This interceptor will thus only be active on the wsproxy endpoint (only serving the inbound side, in this example the external facing side) and only for service x. For securing the calls to service y, we would register Wss4jSecurityInterceptor in OutboundInterceptors, adding message security for messages being sent to the external service y by the wsproxy module.

Ok, that’s about it! Feel free to drop me a message if this was somehow useful or if you have ideas for improvement!

Koen Serneels

Koen Serneels