无法在 Spring 引导中使用 Spring-WS 使用 SOAP WS,但可以在 SOAPUI 中使用

Fail to consume SOAP WS with Spring-WS in Spring boot but works from SOAPUI

我正在使用 Spring 引导来使用我从 WSDL 生成的 SOAP WS。我添加了 spring-ws-security 以便我可以将 user/password 作为安全性 header 传递,如配置中所示:

@Configuration
public class ClientConfig {

    public static final String SIEBEL_ENDPOINT = "http://...";

    @Bean
    public CustomerClient customerClient() {
        CustomerClient client = new CustomerClient();
        client.setDefaultUri(SIEBEL_ENDPOINT);
        client.setWebServiceTemplate(webServiceTemplate(marshaller()));
        return client;
    }

    @Bean
    public WebServiceTemplate webServiceTemplate(Jaxb2Marshaller marshaller) {
        WebServiceTemplate template = new WebServiceTemplate(marshaller, marshaller);
        template.setDefaultUri(SIEBEL_ENDPOINT);
        ClientInterceptor[] interceptors = new ClientInterceptor[] {new LogHttpHeaderClientInterceptor(), wsSecurityInterceptor()};
        template.setInterceptors(interceptors);
        return template;
    }

    @Bean
    public Jaxb2Marshaller marshaller() {
        Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
        marshaller.setContextPath("com.test.dms.gen");
        return marshaller;
    }

    @Bean
    public Wss4jSecurityInterceptor wsSecurityInterceptor() {
        Wss4jSecurityInterceptor wss4jSecurityInterceptor = new Wss4jSecurityInterceptor();
        wss4jSecurityInterceptor.setSecurementActions(WSHandlerConstants.USERNAME_TOKEN);
        wss4jSecurityInterceptor.setSecurementPasswordType(WSConstants.PW_TEXT);
        wss4jSecurityInterceptor.setSecurementUsername("rf_USER");
        wss4jSecurityInterceptor.setSecurementPassword("rf_USER");
        return wss4jSecurityInterceptor;
    }

}

服务调用:

public class CustomerClient extends WebServiceGatewaySupport {
 
    public CustomerInfoOutput getCustomerInfo(String vin) {
        ObjectFactory request = new ObjectFactory();
        final CustomerInfoInput custInfoInput = request.createCustomerInfoInput();
        custInfoInput.setVINNumber(vin);


        return (CustomerInfoOutput) getWebServiceTemplate().marshalSendAndReceive(ClientConfig.SIEBEL_ENDPOINT, custInfoInput);
    }
}

一切都很好生成,这个记录输出:

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header>
    <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
                   xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
                   SOAP-ENV:mustUnderstand="1">
        <wsse:UsernameToken wsu:Id="UsernameToken-e8f183db-44db-4c0b-90d9-ca57e89225fd">
            <wsse:Username>rf_USER</wsse:Username>
            <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">rf_USER</wsse:Password>
        </wsse:UsernameToken>
    </wsse:Security>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
    <ns3:CustomerInfo_Input xmlns:ns2="http://siebel.com/testdashboard"
                            xmlns:ns3="http://test.com/rf/customerinfo" xmlns:ns4="http://test.com/rf"
                            xmlns:ns5="http://www.siebel.com/xml/IBM%20test%20Dashboard">
        <ns3:VINNumber>123456789</ns3:VINNumber>
    </ns3:CustomerInfo_Input>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

当我使用 SOAP UI 发送此请求时,它运行良好。但是当它使用 WSDL 生成的 objects 发送时,我有这个错误:

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
        xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
    <SOAP-ENV:Body>
        <SOAP-ENV:Fault>
            <faultcode>SOAP-ENV:Server</faultcode>
            <faultstring>There is no active Web Service with operation named
                'http://test.com/rf/customerinfo:CustomerInfo_Input'.(SBL-EAI-04313)
            </faultstring>
            <detail>
                <siebelf:siebdetail xmlns:siebelf="http://www.siebel.com/ws/fault">
                    <siebelf:logfilename>EAIObjMgr_enu_0023_24117286.log</siebelf:logfilename>
                    <siebelf:errorstack>
                        <siebelf:error>
                            <siebelf:errorcode>SBL-EAI-04313</siebelf:errorcode>
                            <siebelf:errorsymbol>IDS_EAI_WS_OP_NOT_FOUND</siebelf:errorsymbol>
                            <siebelf:errormsg>There is no active Web Service with operation named
                                'http://test.com/rf/customerinfo:CustomerInfo_Input'.(SBL-EAI-04313)
                            </siebelf:errormsg>
                        </siebelf:error>
                    </siebelf:errorstack>
                </siebelf:siebdetail>
            </detail>
        </SOAP-ENV:Fault>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

有什么想法吗?

PS:请不要关注 URI,因为我更改了它们,但生成的请求在 SOAPUI.

中工作正常

在@RanjithR的提示下我处理如下:

我使用 Wireshark 跟踪了 SOAP 调用,我发现 Spring WS 不包括 header SOAPAction。下面的 SOF 问题讨论了这一点,以及如何配置它(它会将您带到 spring 文档,该文档很好地解释了这件事)。 Spring-WS client not setting SOAPAction header

第二件事是,即使我添加了 header,它仍然告诉我端点未发送。

在我的 WSDL 中,SOAPAction 定义如下:

<soap:operation soapAction="document/http://test.com/rf/customerinfo:CustomerInfo"></soap:operation>

我的 Spring ws 电话是这样的:

return getWebServiceTemplate().marshalSendAndReceive(ClientConfig.SIEBEL_ENDPOINT,
                customerInfoInput,
                webServiceMessage -> ((SoapMessage)webServiceMessage).setSoapAction("document/http://ripl.com/rforce/customerinfo:CustomerInfo"));

在 Wireshark 中,我有:

SOAPAction: "http://ripl.com/rforce/customerinfo:CustomerInfo"

但是当我跟踪来自 SOAP UI 的同一个调用时,我有: SOAPAction: "document/http://ripl.com/rforce/customerinfo:CustomerInfo"

所以我尝试将字符串文字作为 SOAPAction 发送:

"\"document/http://ripl.com/rforce/customerinfo:CustomerInfo\""; 请注意操作周围的 \"\" 并且它有效:)

也许它可以帮助必须与 Jurasik Park 系统集成的人...

这个 post 为我指明了正确的方向,为我节省了很多时间。使用上述方法没有为我添加操作 header,但以下方法工作正常:

// add action header

final String ActionHeaderName = "Action";
final String ActionHeaderPrefix = "wsa";
final String ActionHeaderNamespace = "http://www.w3.org/2005/08/addressing";
final String ActionHeaderContent = "http://example/ActionHeader";
var actionName = soapEnvelope.createName(ActionHeaderName, ActionHeaderPrefix, ActionHeaderNamespace);
var actionHeader = soapEnvelope.getHeader().addHeaderElement(actionName);
                                actionHeader.setTextContent(ActionHeaderContent);

非常感谢@Marouane