Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring WebServiceClient with configurable credentials

I want to create a SOAP web service client in Spring with configurable credentials. The same implementation will be used to call different customers with different user/pass authentication. Basic authentication will be used.

Similar to spring ws WebServiceTemplate credentials but with different credentials for every call.

Is there a better way to do this than get the WebServiceMessageSender and set the credentials every time? If I do so what happens with requests done in parallel to other customers?

Current Configuration

@Bean
public WebServiceTemplate webServiceTemplate(){
    WebServiceTemplate template = new WebServiceTemplate();
    template.setMessageSender(messageSender());
    return template;
}

@Bean
public HttpComponentsMessageSender messageSender(){
    HttpComponentsMessageSender sender = new HttpComponentsMessageSender();
    return sender;
}

Web Service Client

public Status updateStatus(URL url, String user, String password,PackageStatus request){
    WebServiceTemplate template = getWebServiceTemplate();
    //TODO set credentials here ???
    return (Status) template.marshalSendAndReceive(request);
}

Thank you very much for your help, Neo

like image 244
NeoP5 Avatar asked Sep 02 '25 05:09

NeoP5


1 Answers

The most general solution is to create a regular Java factory class that would create web service template given the credentials. Below is an example that uses apache HttpComponents version 4.3:

class WebServiceTemplateFactory {
    //@Autowired - all dependencies that don't change, such as WebServiceMessageFactory,  Marshaller etc.

    WebServiceTemplate createWebServiceTemplate(String user, String pwd) throws Exception {
            WebServiceTemplate webServiceTemplate = new WebServiceTemplate();

            //create the HTTP client 
            RequestConfig requestConfig = RequestConfig.custom().setStaleConnectionCheckEnabled(true).build(); //can set more stuff like connection timeout etc.
            SSLContext sslContext =  SSLContexts.custom().build();
            SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext, null, null, SSLConnectionSocketFactory.STRICT_HOSTNAME_VERIFIER);
            BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
            credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(user, pwd));
            CloseableHttpClient httpClient = HttpClients.custom()
                    .setDefaultRequestConfig(requestConfig)
                    .setSSLSocketFactory(sslSocketFactory)
                    .setDefaultCredentialsProvider(credentialsProvider)
                    .addInterceptorFirst(new HttpComponentsMessageSender.RemoveSoapHeadersInterceptor()) //preventing 'org.apache.http.ProtocolException: Content-Length header already present' exception
                    .build();

            //create the message sender
            HttpComponentsMessageSender messageSender = new HttpComponentsMessageSender(httpClient);
            messageSender.afterPropertiesSet(); // just for consistency - not expecting much from this call

            webServiceTemplate.setMessageSender(messageSender);
            //... set the rest of dependencies, if needed
            return webServiceTemplate;
    }
}

This is the most basic solution that I see. You can optimize it by not creating a separate client for the same username. You can also probably have a single client for all of them - check Apache HttpComponents documentation (or the documentation of the other underlying client that you use).

Note that Spring-WS does not implement the client by itself, it is just a wrapper over some existing HTTP client.

Update:
If you are using Apache HttpClient, check their usage of AuthScope. You can create a single HttpClient for all destinations (host, port), each having its own username/password pair. But you have to know them in advance. If this is the case, then the above code (modified to set all AuthScope/credentials pair) can be used to create a regular Spring bean. The right username/password pair will be picked automatically by Apache HttpClient based on the destination.

like image 161
Alexander Avatar answered Sep 04 '25 17:09

Alexander