无法使用 Spring 的 WebServiceTemplate 将 Http Headers 添加到消息

Cannot Add Http Headers to Message with Spring's WebServiceTemplate

我有一个相当简单的案例,我尝试将 HTTP headers(不是 SOAP headers)添加到我使用 Spring 的 WebServiceTemplate.

我定义了一个 ClientInterceptor 我正在做的事情:

@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;
        }

这就是我配置扩展 WebServiceConfigurationSupport:

SomeClient 的方式
@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;
}

但是 Authorization header 没有被添加。我也试过 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);
    }
}

但似乎效果不佳。我做错了什么,为什么没有添加 Authorization header?谢谢!

使用 HeadersAwareSenderWebServiceConnection 接口而不是实际的底层连接。

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

现在,如果您 upgrade/switch HTTP 库,则无需更改此代码。

要回答有关您做错了什么的问题,就是您投错了 class。是的,您正在使用的 class 已 弃用 但它是您正在使用的库的一部分,您不能在不更改基础 HTTP 的情况下转换为不同的 class图书馆。

我以前做的就是用这样的WebServiceMessageCallback

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

然后我就这样使用了:

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

通过这种方式,我成功地设置了所有需要的 HttpHeader(在我的例子中只有一个 :))

希望有用

安杰洛

长话短说;博士 您的 messageSender 应该是 HttpComponentsMessageSender instead of HttpUrlConnectionMessageSender 的实例。您还需要提供适当的凭据。

getConnection() function of TransportContext returns an implementation of WebServiceConnection. Both HttpUrlConnection and HttpComponentsConnection 是相同的实现。所以基本上你得到了错误的连接类型,因此 ClassCastException.

ClientInterceptor 将适用于自定义 header,但不适用于 Authorization header。为此,您的 HttpComponentsMessageSender 需要使用您的凭据进行配置。

正确的配置应该像 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);
}

我进行了类似的练习,endpointInterceptor 连接 returns HttpServletConnection。因此使用了以下内容并设法添加了 HTTP headers。

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

一些额外的提示:

  1. 如果您想发回与请求中收到的相同 header,请在 endpointInterceptor
  2. handleResponse 方法中使用以下内容
response.addHeader("myheader", request.getHeader("myheader"));
  1. 如果您尝试在 clientInterceptor 中添加自定义 header 以发送到下面的 handleRequest 方法中的下游使用,
HttpUrlConnection connection = (HttpUrlConnection)context.getConnection();
connection.addRequestHeader("myheader", "myvalue");