Sometimes when accessing SOAP APIs, our SOAP client needs to sign the request. How this can be achieved using Spring Boot’s WebserviceTemplate within a few steps is the scope of this short article.
Tip
|
This snippet only deals with the client side not with the security configuration on the server side. Also it assumes, that you have already set up your keystore/truststore and that you’re loading these with your Spring Boot application’s startup without errors. |
Using Maven, we need the following two dependencies in our pom.xml
:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web-services</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.ws</groupId>
<artifactId>spring-ws-security</artifactId>
</dependency>
The following configuration sets up our CryptoFactoryBean, the SecurityInterceptor for our SOAP requests and finally the WebserviceTemplate:
package com.hascode.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.webservices.client.WebServiceTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
import org.springframework.ws.client.core.WebServiceTemplate;
import org.springframework.ws.client.support.interceptor.ClientInterceptor;
import org.springframework.ws.soap.security.wss4j2.Wss4jSecurityInterceptor;
import org.springframework.ws.soap.security.wss4j2.support.CryptoFactoryBean;
import java.io.IOException;
@Configuration
public class WssConfiguration {
private static final Logger log = LoggerFactory.getLogger(WssConfiguration.class);
private final Resource keystore;
private final String keystorePassword;
private final String signatureUser = "foo";
public WssConfiguration(@Value("${server.ssl.key-store}") Resource keystore,
@Value("${server.ssl.key-store-password}") String keystorePassword) {
this.keystore = keystore;
this.keystorePassword = keystorePassword;
}
@Bean
public CryptoFactoryBean getCryptoFactoryBean() throws IOException {
CryptoFactoryBean cryptoFactoryBean = new CryptoFactoryBean();
cryptoFactoryBean.setKeyStorePassword(keystorePassword);
cryptoFactoryBean.setKeyStoreLocation(keystore);
return cryptoFactoryBean;
}
@Bean
public Wss4jSecurityInterceptor serverSecurityInterceptor() {
var securityInterceptor = new Wss4jSecurityInterceptor();
try {
securityInterceptor
.setValidationSignatureCrypto(getCryptoFactoryBean().getObject());
} catch (Exception e) {
log.error("error accessing the truststore", e);
}
securityInterceptor.setSecurementActions("Signature"); // Signing only, no Timestamp
securityInterceptor.setSecurementUsername(signatureUser);
securityInterceptor.setSecurementPassword(keystorePassword);
try {
securityInterceptor
.setSecurementSignatureCrypto(getCryptoFactoryBean().getObject());
} catch (Exception e) {
log.error("error accessing the keystore", e);
}
return securityInterceptor;
}
@Bean
public WebServiceTemplate getWebServiceTemplate(Jaxb2Marshaller marshaller) {
var webServiceTemplate = new WebServiceTemplateBuilder().build();
ClientInterceptor[] interceptors = new ClientInterceptor[] {serverSecurityInterceptor()};
webServiceTemplate.setInterceptors(interceptors);
webServiceTemplate.setMarshaller(marshaller);
webServiceTemplate.setUnmarshaller(marshaller);
return webServiceTemplate;
}
}
logging.level.org.springframework.web=DEBUG
logging.level.org.springframework.ws.client.MessageTracing.sent=DEBUG
logging.level.org.springframework.ws.server.MessageTracing.sent=DEBUG
logging.level.org.springframework.ws.client.MessageTracing.received=TRACE
logging.level.org.springframework.ws.server.MessageTracing.received=TRACE
Sending a SOAP request with the WebserviceTemplate
now should produce a WSS compliant SOAP request with a signed envelope.
Resources: