首页 > 解决方案 > 将 ws-security 添加到 Mule 中的流

问题描述

  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 配置,添加了“签名”条目,选择了先前添加的密钥库,带有别名和密码,并且它在那里工作。但我不能让它在骡子上工作,因此是个问题。


据我所知,我应该:

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


已编辑(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">.

标签: web-servicessoapmulemule-esb

解决方案


如果可能的话,我会避免使用 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/


推荐阅读