Cannot Add Http Headers to Message with Spring's WebServiceTemplate

吃可爱长大的小学妹 提交于 2019-12-24 10:08:22

问题


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!


回答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.




回答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




回答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);
}


来源:https://stackoverflow.com/questions/48623512/cannot-add-http-headers-to-message-with-springs-webservicetemplate

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!