添加Wss4jSecurityInterceptor配置后,不会调用Soap Fault Message Resolver

时间:2014-05-16 19:53:09

标签: spring webservice-client

我编写了一个与第三方Web服务一起使用的Web服务客户端(使用Java Spring和JAXB Marshaller)。当我发送有效请求时,一切正常。当我发送无效请求时,Web服务服务器会响应SOAP Fault。客户端应用程序失败并出现UnmarshallingFailureException

org.springframework.oxm.UnmarshallingFailureException: JAXB unmarshalling 
exception; nested exception is javax.xml.bind.UnmarshalException: 
unexpected element (uri:"http://schemas.xmlsoap.org/soap/envelope/", local:"Fault").

对我来说,我的ws客户端无法破译Web服务返回的SOAP错误。我编写了一个自定义的FaultMessageResolver,但它没有被调用(我在那里设置了一个断点,但它没有被击中。在添加Wss4jSecurityInterceptor进行签名,加密/解密之前,FaultMessageResolver工作正常。)这是代码:

public class VehicleServiceClientExceptionResolver implements FaultMessageResolver {

@Override
public void resolveFault(WebServiceMessage message) throws IOException {

    SoapMessage soapMessage = (SoapMessage) message;

    try {

        JAXBContext context = JAXBContext.newInstance(ErrorMessages.class);
        Unmarshaller unMarshaller = context.createUnmarshaller();
        ErrorMessages errorMessages = (ErrorMessages)unMarshaller.unmarshal(soapMessage.getSoapBody().getFault().getFaultDetail().getDetailEntries().next().getSource());

        if (errorMessages.getErrorMessage().size() > 0) {
            throw new VehicleServiceClientException(errorMessages);
        }

    } catch (JAXBException e) {
        LOGGER.debug(e.getMessage());
    }
}

}

此自定义soap故障解析器将注入客户端Web服务模板,如下所示:

<bean id="vehicleQuotationWebServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
    <constructor-arg ref="messageFactory"/>
    <property name="interceptors">
        <list>
            <ref bean="wsSecurityInterceptor"/>
        </list> 
    </property>
    <property name="marshaller" ref="vehicleQuotationMarshaller" />
    <property name="unmarshaller" ref="vehicleQuotationMarshaller" />
    <property name="messageSender" ref="urlMessageSender"/>
    <property name="faultMessageResolver" ref="vehicleServiceClientFaultMessageResolver" />
    <property name="defaultUri" value="https://*********/*********Service"/>
</bean>

最奇怪的是,虽然我得到了unmarshall异常,但是当我将日志级别从INFO更改为DEBUG时,我确实看到加密的服务器响应在我的eclipse控制台中被解密,我不知道这个DigesterOutputStream来自哪里,但是我认为这可能是解决这个问题的关键。

任何人都有任何想法?谢谢!

DEBUG p.xml.dsig.internal.DigesterOutputStream:
<soapenv:Body xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="Id-af090516-9e00-4590-b481-c78e59d6b2fc"><soapenv:Fault><faultcode>soapenv:Client.Validation</faultcode><faultstring</faultstring><detail><em:ErrorMessages xmlns:em="urn:ford/errormessage/v1.0"><em:ErrorMessage><em:ErrorCode>GLSE903100</em:ErrorCode><em:ErrorDescription> CTT System Quote Id already exists ('1041')</em:ErrorDescription><em:ErrorTime>2014-05-16T15:13:20</em:ErrorTime></em:ErrorMessage></em:ErrorMessages></detail></soapenv:Fault></soapenv:Body>

2 个答案:

答案 0 :(得分:2)

我找到了解决方案here:将WebServiceMessageExtractor<Object>添加到:

WebServiceTemplate.sendAndReceive(
    new WebServiceMessageCallback(),
    new WebServiceMessageExtractor<Object>())

诀窍。

答案 1 :(得分:1)

另一种解决方案:

public class ExampleInterceptor implements ClientInterceptor {
  public boolean handleResponse(MessageContext messageContext) throws WebServiceClientException {
    var resp = (SoapMessage) messageContext.getResponse();
    Optional.of(resp)
         .filter(res -> !hasFault(res))
         .orElseThrow(() -> new SoapFaultClientException(resp));
    return true;
  }

  private boolean hasFault(final WebServiceMessage response) {
  return Optional.ofNullable(response)
      .filter(resp -> resp instanceof FaultAwareWebServiceMessage)
      .map(resp -> (FaultAwareWebServiceMessage) resp)
      .map(FaultAwareWebServiceMessage::hasFault)
      .orElse(false);
  }
}


@Configuration
public class ExampleConnectorConfig extends WSConnectorConfig
@Bean
  public WSConnector soapConnector(Jaxb2Marshaller marshaller) {
    WSConnector client = new WSConnector(messageFactory());
    client.setInterceptors(new ClientInterceptor[]{new ExampleInterceptor()});
    client.setDefaultUri(proxy);
    return client;
  }
  //Example
  @Bean
  public SaajSoapMessageFactory messageFactory() {
    SaajSoapMessageFactory messageFactory = new SaajSoapMessageFactory();
    messageFactory.afterPropertiesSet();
    return messageFactory;
  }
}