'Cannot Add Http Headers to Message with Spring's WebServiceTemplate

I have a fairly simple case where I am trying to add HTTP headers (not SOAP headers) to a request I am making using Spring's WebServiceTemplate.

I have defined a ClientInterceptor where I am doing:

@Override
    public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {

        try {
            TransportContext context = TransportContextHolder.getTransportContext();
            HttpComponentsConnection connection = (HttpComponentsConnection) context.getConnection();
            connection.addRequestHeader("Authorization", String.format("Bearer %s", someAccessToken));
        } catch (IOException exception) {
            // Do nothing
        }

        return true;
        }

This is how I configure my SomeClient which extends WebServiceConfigurationSupport:

@Bean
public SomeClient someClient() {

    ...

    SomeClientImpl service =  new SomeClientImpl();

    service.setObjectFactory(new com.path.ObjectFactory());
    service.setDefaultUri(someUri);
    service.setMarshaller(marshaller);
    service.setUnmarshaller(marshaller);
    service.setxStreamMarshaller(xStreamMarshaller);
    service.setInterceptors(new ClientInterceptor[]{wss4jSecurityInterceptor()});
    service.setMessageSender(new HttpComponentsMessageSender());
    service.setInterceptors(new ClientInterceptor[]{wss4jSecurityInterceptor(), addHttpHeaderInterceptor()});
    return service;
}

@Bean
public ClientInterceptor addHttpHeaderInterceptor() {
    return new AddHttpHeaderInterceptor(someAccessToken);
}

@Bean
public Wss4jSecurityInterceptor wss4jSecurityInterceptor() {

    Wss4jSecurityInterceptor interceptor = new Wss4jSecurityInterceptor();

    interceptor.setSecurementActions(securementAction);
    interceptor.setSecurementUsername(securementUsername);
    interceptor.setSecurementPassword(securementPassword);
    interceptor.setSecurementPasswordType(WSConstants.PW_TEXT);
    interceptor.setSecurementMustUnderstand(false);

    return interceptor;
}

But the Authorization header is not being added. I have also tried with a CustomMessageCallback:

public class CustomMessageCallback implements WebServiceMessageCallback {
    private String headerKey;
    private String headerValue;

    public CustomMessageCallback(String headerKey, String headerValue) {
        this.headerKey = headerKey;
        this.headerValue = headerValue;
    }

    @Override
    public void doWithMessage(WebServiceMessage webServiceMessage) throws IOException, TransformerException {

        TransportContext context = TransportContextHolder.getTransportContext();
        HttpComponentsConnection conn = (HttpComponentsConnection) context.getConnection();

        HttpPost post = conn.getHttpPost();
        post.addHeader(headerKey, headerValue);
    }
}

But it does not seem to work as well. What am I doing wrong, why the Authorization header is not being added? Thanks!



Solution 1:[1]

Use the HeadersAwareSenderWebServiceConnection interface instead of the actual underlying connection.

TransportContext context = TransportContextHolder.getTransportContext();
HeadersAwareSenderWebServiceConnection connection = (HeadersAwareSenderWebServiceConnection) context.getConnection();
connection.addRequestHeader("Authorization", String.format("Bearer %s", "********"));

Now if you upgrade/switch HTTP library you don't need to change this code.

To answer your question about what you are doing wrong is that you are casting to the wrong class. Yes the class you are using is deprecated but it is part of the library you are using, you cannot just cast to a different class without changing the underlying HTTP library.

Solution 2:[2]

What I did in past is to use a WebServiceMessageCallback like this one:

public class WsHttpHeaderCallback implements WebServiceMessageCallback
{
    private String headerKey;
    private String headerValue;
    private String soapAction;

    public WsHttpHeaderCallback(String headerKey, String headerValue, String soapAction)
    {
        super();
        this.headerKey = headerKey;
        this.headerValue = headerValue;
        this.soapAction = soapAction;
        validateRequiredFields();
    }

    public WsHttpHeaderCallback()
    {
        super();
    }

    @Override
    public void doWithMessage(WebServiceMessage message) throws IOException, TransformerException
    {
        validateRequiredFields();
        addRequestHeader(headerKey, headerValue);
        if (StringUtils.hasText(this.soapAction))
        {
            AxiomSoapMessage axiomMessage = (AxiomSoapMessage) message;
            axiomMessage.setSoapAction(this.soapAction);
        }       
    }
    private void validateRequiredFields()
    {
        if( !StringUtils.hasText(headerKey) )
        {
            throw new IllegalArgumentException("Impossibile proseguire. Passato HEADER HTTP con chiave non valida: ["+headerKey+"]");
        }
        if( !StringUtils.hasText(headerValue) )
        {
            throw new IllegalArgumentException("Impossibile proseguire. Passato HEADER HTTP con valore non valido: ["+headerValue+"]");
        }       
    }
    private void addRequestHeader(String headerKey, String headerValue)
    {
        TransportContext context = TransportContextHolder.getTransportContext();
        WebServiceConnection connection = context.getConnection();
        if (connection instanceof HttpComponentsConnection)
        {
            HttpComponentsConnection conn = (HttpComponentsConnection) connection;
            HttpPost post = conn.getHttpPost();
            post.addHeader(headerKey, headerValue); 
        }
        else if( connection instanceof ClientHttpRequestConnection )
        {
            ClientHttpRequestConnection conn = (ClientHttpRequestConnection)connection;
            conn.getClientHttpRequest().getHeaders().add(headerKey, headerValue);
        }
    }   
}

Then I used it in this way:

wsTemplate.marshalSendAndReceive(wsUrl, request, new WsHttpHeaderCallback(headerKey, headerValue, soapAction) );

In this way I successfully set all the needed HttpHeaders (in my case just one :) )

I hope it is usefull

Angelo

Solution 3:[3]

TL;DR Your messageSender should be an instance of HttpComponentsMessageSender instead of HttpUrlConnectionMessageSender. Also you need to provide proper credentials.

getConnection() function of TransportContext returns an implementation of WebServiceConnection. Both HttpUrlConnection and HttpComponentsConnection are implementations of the same. So basically you are getting the wrong type of connection,hence the ClassCastException.

The ClientInterceptor will work for custom headers but not for Authorization header. For that, your HttpComponentsMessageSender needs to be configured with your credentials.

The proper configuration should be like this

@Value("${username}")
private String username;

@Value("${password}")
private String password;

@Bean
public SomeClient someClient() {

  SomeClientImpl service =  new SomeClientImpl();
  service.setMessageSender();
  //other configs

  return service;
}

public HttpComponentsMessageSender getMessageSender(){
  HttpComponentsMessageSender httpComponentsMessageSender = new HttpComponentsMessageSender();
  httpComponentsMessageSender.setCredentials(getCredentials);
}

public UsernamePasswordCredentials getCredentials(){
  return new UsernamePasswordCredentials(username, password);
}

Solution 4:[4]

I went through a similar exercise, for an endpointInterceptor the connection returns a HttpServletConnection. Therefore used the following and managed to get the HTTP headers added.

HttpServletConnection connection = (HttpServletConnection)context.getConnection();
        HttpServletResponse response = connection.getHttpServletResponse();
        HttpServletRequest request = connection.getHttpServletRequest();
        response.addHeader("myheader", "myvalue");

Some additional tips:

  1. If you want to send back the same header you received in the request, use following in the handleResponse method of the endpointInterceptor
response.addHeader("myheader", request.getHeader("myheader"));
  1. If you are trying to add custom headers in an clientInterceptor to send to a downstream use below in the handleRequest method,
HttpUrlConnection connection = (HttpUrlConnection)context.getConnection();
connection.addRequestHeader("myheader", "myvalue");

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Angelo Immediata
Solution 3
Solution 4 maheeka