title: How to secure a Spring soap service with certificates
body: <p>I managed to create a soap server and client that requires a username and password to call functions. The following code shows how i've done this</p>
  
  <p><strong>Client:</strong>    </p>
  
  <pre><code>package hello;
  
  import org.springframework.context.annotation.Bean;
  import org.springframework.context.annotation.Configuration;
  import org.springframework.core.io.ClassPathResource;
  import org.springframework.oxm.jaxb.Jaxb2Marshaller;
  import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
  import org.springframework.security.core.context.SecurityContextHolder;
  import org.springframework.security.core.context.SecurityContextImpl;
  import org.springframework.ws.client.support.interceptor.ClientInterceptor;
  import org.springframework.ws.soap.security.xwss.XwsSecurityInterceptor;
  import org.springframework.ws.soap.security.xwss.callback.SpringUsernamePasswordCallbackHandler;
  
  @Configuration
  public class SoftlayerConfiguration {
  
      @Bean
      public Jaxb2Marshaller marshaller() {
          Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
          marshaller.setContextPath("be.elision.soap.cloud");
          return marshaller;
      }
  
      @Bean
      public SoftlayerClient softlayerClient(Jaxb2Marshaller marshaller) {
          SoftlayerClient client = new SoftlayerClient();
          client.setDefaultUri("http://192.168.137.107:8080/ws");
          client.setMarshaller(marshaller);
          client.setUnmarshaller(marshaller);
          client.setInterceptors(new ClientInterceptor[]{securityInterceptor()});
          return client;
      }
  
      @Bean
      XwsSecurityInterceptor securityInterceptor() {
          XwsSecurityInterceptor securityInterceptor = new XwsSecurityInterceptor();
          securityInterceptor.setSecureRequest(true);
          securityInterceptor.setValidateRequest(true);
          securityInterceptor.setCallbackHandler(callbackHandler());
          securityInterceptor.setPolicyConfiguration(new ClassPathResource("securityPolicy.xml"));
          return securityInterceptor;
      }
  
      @Bean
      SpringUsernamePasswordCallbackHandler callbackHandler() {
          SecurityContextHolder.setContext(new SecurityContextImpl());
          SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken("user", "password"));
          return new SpringUsernamePasswordCallbackHandler();
      }
  
  }
  </code></pre>
  
  <p><strong>Server:</strong></p>
  
  <pre><code>package be.elision.main;
  
  import java.util.Collections;
  import java.util.List;
  
  import org.springframework.boot.context.embedded.ServletRegistrationBean;
  import org.springframework.context.ApplicationContext;
  import org.springframework.context.annotation.Bean;
  import org.springframework.context.annotation.Configuration;
  import org.springframework.core.io.ClassPathResource;
  import org.springframework.ws.config.annotation.EnableWs;
  import org.springframework.ws.config.annotation.WsConfigurerAdapter;
  import org.springframework.ws.server.EndpointInterceptor;
  import org.springframework.ws.server.endpoint.interceptor.PayloadLoggingInterceptor;
  import org.springframework.ws.soap.security.xwss.XwsSecurityInterceptor;
  import org.springframework.ws.soap.security.xwss.callback.SimplePasswordValidationCallbackHandler;
  import org.springframework.ws.soap.server.endpoint.interceptor.PayloadValidatingInterceptor;
  import org.springframework.ws.transport.http.MessageDispatcherServlet;
  import org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition;
  import org.springframework.xml.xsd.SimpleXsdSchema;
  import org.springframework.xml.xsd.XsdSchema;
  
  @EnableWs
  @Configuration
  public class WebServiceConfig extends WsConfigurerAdapter {
      @Bean
      public ServletRegistrationBean messageDispatcherServlet(
              ApplicationContext applicationContext) {
          MessageDispatcherServlet servlet = new MessageDispatcherServlet();
          servlet.setApplicationContext(applicationContext);
          servlet.setTransformWsdlLocations(true);
          return new ServletRegistrationBean(servlet, "/ws/*");
      }
  
      @Bean(name = "devices")
      public DefaultWsdl11Definition defaultWsdl11Definition(
              XsdSchema devicesSchema) {
          DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();
          wsdl11Definition.setPortTypeName("DevicesPort");
          wsdl11Definition.setLocationUri("/ws");
          wsdl11Definition.setTargetNamespace("http://elision.be/soap/cloud");
          wsdl11Definition.setSchema(devicesSchema);
          return wsdl11Definition;
      }
  
      // toevoegen van xsd aan bean
      @Bean
      public XsdSchema devicesSchema() {
          return new SimpleXsdSchema(new ClassPathResource("devices.xsd"));
      }
  
      @Bean
      XwsSecurityInterceptor securityInterceptor() {
          XwsSecurityInterceptor securityInterceptor = new XwsSecurityInterceptor();
          securityInterceptor.setCallbackHandler(callbackHandler());
          securityInterceptor.setPolicyConfiguration(new ClassPathResource(
                  "securityPolicy.xml"));
          return securityInterceptor;
      }
  
      @Bean
      SimplePasswordValidationCallbackHandler callbackHandler() {
          SimplePasswordValidationCallbackHandler callbackHandler = new SimplePasswordValidationCallbackHandler();
          callbackHandler.setUsersMap(Collections
                  .singletonMap("user", "password"));
          return callbackHandler;
      }
  
      @Bean
      PayloadLoggingInterceptor payloadLoggingInterceptor() {
          return new PayloadLoggingInterceptor();
      }
  
      @Bean
      PayloadValidatingInterceptor payloadValidatingInterceptor() {
          final PayloadValidatingInterceptor payloadValidatingInterceptor = new PayloadValidatingInterceptor();
          payloadValidatingInterceptor.setSchema(new ClassPathResource(
                  "devices.xsd"));
          return payloadValidatingInterceptor;
      }
  
      @Override
      public void addInterceptors(List&lt;EndpointInterceptor&gt; interceptors) {
          interceptors.add(payloadLoggingInterceptor());
          interceptors.add(payloadValidatingInterceptor());
          interceptors.add(securityInterceptor());
      }
  
  }
  </code></pre>
  
  <p><strong>Security policy:</strong></p>
  
  <pre><code>&lt;xwss:SecurityConfiguration xmlns:xwss="http://java.sun.com/xml/ns/xwss/config"&gt;
      &lt;xwss:UsernameToken digestPassword="false" useNonce="false" /&gt;
  &lt;/xwss:SecurityConfiguration&gt;
  </code></pre>
  
  <p>But now i want to replace this username and password authentication with a certificate. I prefere not to do this in xml, but rather implement this in my existing code as shown above.</p>