3
  1. 我正在使用没有任何工作室的骡子。
  2. mule 中暴露的 web 服务。客户端发送请求,我转换消息(fe 填写一些字段)并将其转发到外部 Web 服务(不由我管理)。
  3. 用于外部 Web 服务的 Java 类是使用 wsimport 基于它们的 wsdl 创建的
  4. 他们需要基于他们给我的密钥库的 wss xml 签名
  5. 他们的服务(和 wsdl)可以通过 http 访问
  6. 我的目标:我想添加 ws-security(Signature现在只是行动)以便能够向他们的 WS 发出请求。
  7. 总结:客户端向我拥有的 mule (http) 上的 WS 发送请求,我对其进行转换,添加 ws-security 并将其发送到外部 Web 服务 (https)。

这是我的 WS:

@WebService
public interface LFlow {

@WebMethod
String exec(@WebParam(name = "pname") String pname,
           @WebParam(name = "mname") String mname,
           @WebParam(name = "parameters") String parameters);
}

和请求:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:abc="http://a.b.c/">
   <soapenv:Header/>
   <soapenv:Body>
      <abc:exec>
         <pname>B</pname>
         <mname>getC</mname>
         <parameters>smth</parameters>
      </abc:exec>
   </soapenv:Body>
</soapenv:Envelope>

这是我为此准备的骡子配置:

<?xml version="1.0" encoding="UTF-8"?>
<mule <!--(...)--> >

    <context:property-placeholder location="classpath:l.properties, classpath:wss.properties"
                                  ignore-resource-not-found="true"/>

(...)

    <flow name="lFlow">
        <!-- Defined in other place: <http:connector name="domain-http-connector" /> -->
        <http:inbound-endpoint connector-ref="domain-http-connector" address="${l.bind.address}" exchange-pattern="request-response"
                               doc:name="HTTP">
            <cxf:jaxws-service serviceClass="a.b.c.LFlow" mtomEnabled="true">
                <cxf:outFaultInterceptors>
                    <spring:bean class="a.b.c.interceptors.FaultSoapInterceptor"/>
                </cxf:outFaultInterceptors>
            </cxf:jaxws-service>
        </http:inbound-endpoint>
        <choice doc:name="Forward to proper process">
            <!--(...)-->
            <when expression="#[payload[0] == 'B']">
                <flow-ref name="b" doc:name="b"/>
            </when>
        </choice>
    </flow>

    <!--(...)-->

    <sub-flow name="b">
        <choice doc:name="forward to proper method">
            <!--(...)-->
            <when expression="#[payload[1] == 'getC']">
                <invoke object-ref="aHandler" method="getC" doc:name="Get C element"
                        methodArgumentTypes="java.lang.String" methodArguments="#[payload[2]]"/>
            </when>
        </choice>
    </sub-flow>

</mule>

wss.properties文件位于src/main/resources目录中,如下所示:

org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin > org.apache.ws.security.crypto.merlin.keystore.type=jks > org.apache.ws.security.crypto.merlin.keystore.password=pass > org.apache.ws.security.crypto.merlin.keystore.alias=别名 org.apache.ws.security.crypto.merlin.file=path-to-keystore

我这边的处理程序进行了一些转换(为简单起见,不在此示例中):

@Component
public class AHandler {

    private final AService aService;

    @Autowired
    public AHandler(final AService aService) {
        this.aService = aService;
    }

    public GetCResponse getC(final String jsonPayload) {
        return aService.getC(mapToGetCRequest(jsonPayload));
    }
}

然后将其路由到应该将其发送到外部 Web 服务的类:

@Service
public class AServiceImpl implements AService {

    // (...)

    @Override
    public GetCResponse getC(final GetCRequest request) {
        return getInstance().getC(request);
    }

    private BInterface getInstance() {
        /**
         I have generated *.jar for the external B service using wsimport from their wsdl-url
        */

        final B service = new B();
        final BInterface port = service.getBSOAP();
        final BindingProvider bindingProvider = (BindingProvider) port;

        final Map<String, Object> context = bindingProvider.getRequestContext();

        context.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, /*wsdl-url*/);
        context.put("thread.local.request.context", "true");

        return port;
    }

}

这是wsdl的一部分:

(...) <?xml name=B>
  </wsdl:types>
  <wsdl:message name="GetCRequest">
    <wsdl:part element="b:GetCRequest" name="payload">
    </wsdl:part>
  </wsdl:message>
  <wsdl:message name="GetCResponse">
    <wsdl:part element="b:GetCResponse" name="payload">
    </wsdl:part>
  
  <wsdl:portType name="BInterface">
    
    <wsdl:operation name="getC">
      <wsdl:input message="b:GetCRequest">
    </wsdl:input>
      <wsdl:output message="b:GetCResponse">
    </wsdl:output>
    </wsdl:operation>
  </wsdl:portType>
  <wsdl:binding name="BSOAPBinding" type="b:BInterface">
    <soap:binding style="b" transport="http://schemas.xmlsoap.org/soap/http"/>
    <wsdl:operation name="getC">
      <wsdl:input>
        <soap:body use="literal"/>
      </wsdl:input>
      <wsdl:output>
        <soap:body use="literal"/>
      </wsdl:output>
    </wsdl:operation>
  </wsdl:binding>
  <wsdl:service name="B">
    <wsdl:port binding="b:BSOAPBinding" name="BSOAP">
      <soap:address location="https://b.local/cxf/ba/b"/>
    </wsdl:port>
  </wsdl:service>
</wsdl:definitions>

通讯在soapui工作。我添加了传出 ws-security 配置,添加了“签名”条目,选择了先前添加的密钥库,带有别名和密码,并且它在那里工作。但我不能让它在骡子上工作,因此是个问题。


据我所知,我应该:

  • WSS4JOutInterceptor使用已配置的: action, signatureUser,进行创建signaturePropFilepasswordCallbackRef并以某种方式将其与 mule 传出消息或

  • 添加<jaxws-client><proxy-client>嵌入 wss 配置:

         <cxf:ws-security>
             <cxf:ws-config>
                 <cxf:property key="action" value="Signature"/>
                 <cxf:property key="signaturePropFile" value="wss.properties"/>
                 <cxf:property key="passwordCallbackClass" value="com.mulesoft.mule.example.security.PasswordCallback"/>
             </cxf:ws-config>
         </cxf:ws-security>
    

但我就是不能让它工作。我真的很感激任何帮助


已编辑(24.08.2021T12:03:00Z):

  1. 骡子sdk3.8.0
  2. 回答当我应用我的解决方案时会发生什么很复杂。我不确定应该将 wss 配置放在哪里。我应该使用<jaxws-client>or<proxy-client>吗?它应该在<subflow>or中<flow>吗?我应该通过这些元素的哪些“属性”(最低限度,需要测试它是否有效)?或者我应该使用<cxf:inInterceptors>/ <cxf:outInterceptors>

我尝试了不同的配置,但我不确定我是否以正确的方式进行操作,所以我得到的错误可能是我使用不当造成的。所以我没有把它们放在这里,因为它们可能会使我的问题更难阅读。


已编辑(24.08.2021T12:54:00Z):

但根据文档

proxy-client为传出的 XML 消息提供原始 SOAP 和 WS-* 处理,允许您以原始 XML 格式发送传出消息并将诸如 WS-Security 之类的东西应用于它们。

我应该使用<proxy-client>并且我相信它应该在“子流”中:

<sub-flow name="b">
    <cxf:proxy-client>
       <cxf:ws-security>
           <cxf:ws-config>
               <cxf:property key="action" value="Signature"/>
               <cxf:property key="signatureUser" value="keystore-alias"/>
               <cxf:property key="signaturePropFile" value="wss.properties"/>
               <cxf:property key="passwordCallbackClass" value="com.mulesoft.mule.example.security.PasswordCallback"/>
           </cxf:ws-config>
       </cxf:ws-security>
    </cxf:proxy-client>
    <choice doc:name="forward to proper method">
        <!--(...)-->
        <when expression="#[payload[1] == 'getC']">
            <invoke object-ref="aHandler" method="getC" doc:name="Get C element"
                    methodArgumentTypes="java.lang.String" methodArguments="#[payload[2]]"/>
        </when>
    </choice>
</sub-flow>

那是对的吗 ?我应该为哪些属性定义<cxf:proxy-client>?或者也许不是在<cxf:ws-security>内部定义,我应该像这样定义拦截器:

<bean id="clientWss4jOutInterceptor" class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor">
    <constructor-arg>
        <map>
            <entry key="action" value="Signature"/>
            <entry key="signatureUser" value=""/>
            <entry key="signaturePropFile" value=""/>
            <entry key="passwordCallbackRef" value-ref=""/>
        </map>
    </constructor-arg>
</bean>
(...)
<sub-flow name="b">
<cxf:proxy-client doc:name="Proxy client">
    <cxf:outInterceptors>
        <spring:bean id="clientWss4jOutInterceptor">
        </spring:bean>
    </cxf:outInterceptors>
</cxf:proxy-client>
(...)

已编辑(25.08.2021T11:28:00Z):

当尝试像这样使用消费者时(mule-config.xml):

<?xml version="1.0" encoding="UTF-8"?>
<mule 
  xmlns:context="http://www.springframework.org/schema/context" 
  xmlns:tls="http://www.mulesoft.org/schema/mule/tls"          
  version="CE-3.8.0"
       
  xmlns:ws="http://www.mulesoft.org/schema/mule/ws"
          
  (...)
  <tls:context name="tlsContext">
    <tls:key-store
                    
      path="C:\\Users\\john\\Documents\\integrationB.keystore"
                    
      keyPassword="privatekey-password"
                    
      password="keystore-password"
                    
      alias="alias" />
  </tls:context>
  
  <!--service, port, wsdl-url, address - all taken from wsdl-->
  <ws:consumer-config 
    name="WebServiceConsumer"
    
    serviceAddress="https://b.local/cxf/ba/b"
     
    wsdlLocation="https://b.local/cxf/ba/b?wsdl"
    
    service="B"
    port="BSOAP">
    <ws:security>
      <ws:wss-sign tlsContext-ref="tlsContext" />
    </ws:security>
  </ws:consumer-config>
  <flow name="lFlow">
    <http:inbound-endpoint 
      connector-ref="domain-http-connector" 
      address="${l.bind.address}" 
      exchange-pattern="request-response"
           
      doc:name="HTTP">
      <cxf:jaxws-service serviceClass="a.b.c.LFlow" mtomEnabled="true">
          <cxf:outFaultInterceptors>
              <spring:bean class="a.b.c.interceptors.FaultSoapInterceptor"/>
          </cxf:outFaultInterceptors>
      </cxf:jaxws-service>
    </http:inbound-endpoint>
    <http:listener config-ref="HTTP_Listener_Configuration" path="*" doc:name="HTTP">
      <http:response-builder statusCode="200"/>
    </http:listener>
    <ws:consumer config-ref="WebServiceConsumer" operation="getC" doc:name="Get C element"/>
    <choice doc:name="Forward to proper process">
        <!--(...)-->
        <when expression="#[payload[0] == 'B']">
            <flow-ref name="b" doc:name="b"/>
        </when>
    </choice>
  </flow>
  <!--(...)-->

    <sub-flow name="b">
        <choice doc:name="forward to proper method">
            <!--(...)-->
            <when expression="#[payload[1] == 'getC']">
                <invoke object-ref="aHandler" method="getC" doc:name="Get C element"
                        methodArgumentTypes="java.lang.String" methodArguments="#[payload[2]]"/>
            </when>
        </choice>
    </sub-flow>
</mule>

我在应用程序启动期间得到:

原因:org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException:来自 URL [file:/(...)/mule-config.xml] 的 XML 文档中的第 46 行无效;嵌套异常是 org.xml.sax.SAXParseException;行号:46;列号:61;cvc-complex-type.2.4.a:发现以元素“ws:consumer-config”开头的无效内容。(...) 之一是预期的。

46:61指向最后一个字符:port="BSOAP">.

4

1 回答 1

1

如果可能的话,我会避免使用 CXF 并尝试使用更容易使用的 Web 服务使用者:

<tls:context name="tlsContext">
   <tls:key-store path="path" keyPassword="pass" password="pass" alias="keyalias" />
</tls:context>

<ws:consumer-config name="Web_Service_Consumerweather" serviceAddress="http://localhost/test" wsdlLocation="Test.wsdl"
                    service="TestService" port="TestPort">
        <ws:security>
            <ws:wss-sign tlsContext-ref="tlsContext" />
        </ws:security>
</ws:consumer-config>

<flow name="listInventory" doc:name="listInventory">
        <http:listener config-ref="HTTP_Listener_Configuration" path="inventory" doc:name="HTTP">
            <http:response-builder statusCode="200"/>
        </http:listener>
        <ws:consumer config-ref="Web_Service_Consumer" operation="ListInventory" doc:name="List Inventory"/>
    </flow>

另请注意,Mule 3.8 已被 Mule 3.9 取代。最新版本是 Mule 4.3,它与 Mule 3.x 不兼容,不支持 CXF。

文档:https ://docs.mulesoft.com/web-service-consumer-connector/0.3.9/

于 2021-08-24T18:24:11.503 回答