【问题标题】:Add ws-security to flow in Mule将 ws-security 添加到 Mule 中的流
【发布时间】:2021-10-24 13:27:25
【问题描述】:
  1. 我正在使用没有任何工作室的 mule。
  2. 在 mule 中公开了 web 服务。客户端发送请求,我转换消息(例如填写一些字段)并将其转发到外部 Web 服务(不由我管理)。
  3. 外部 Web 服务的 Java 类是使用 wsimport 基于它们的 wsdl 创建的
  4. 他们需要基于他们给我的密钥库的 wss xml 签名
  5. 可以通过 https 访问他们的服务(和 wsdl)
  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=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));
    }
}

然后它被路由到应该将它发送到外部网络服务的类:

@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 并配置:actionsignatureUsersignaturePropFilepasswordCallbackRef 并以某种方式将其与 mule 传出消息或

  • 添加 &lt;jaxws-client&gt;&lt;proxy-client&gt; 并嵌入 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. 骡子sdk 3.8.0
  2. 回答当我应用我的解决方案时会发生什么很复杂。我不确定应该将 wss 配置放在哪里。我应该使用 &lt;jaxws-client&gt; 还是 &lt;proxy-client&gt; ?它应该在 &lt;subflow&gt;&lt;flow&gt; 中吗?我应该通过这些元素的哪些“属性”(最低限度,需要测试它是否有效)?或者我应该使用&lt;cxf:inInterceptors&gt;/&lt;cxf:outInterceptors&gt;

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


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

但是根据doc

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

我应该使用&lt;proxy-client&gt;,我相信它应该在“子流”中:

<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>

正确吗?我应该为 &lt;cxf:proxy-client&gt; 定义哪些属性?或者也许不是在里面定义&lt;cxf:ws-security&gt;,我应该像这样定义拦截器:

<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"&gt;

【问题讨论】:

  • Mule 的确切版本是什么?实施您提到的每个解决方案的结果是什么?
  • 编辑了我的问题。但为了清楚起见。骡 sdk 3.8.0.我对解决方案的不当使用可能会误导潜在的帮助者,所以我跳过了它们。当有人指导我应该走哪条路时,我会添加它们。

标签: web-services soap mule mule-esb


【解决方案1】:

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

<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/

【讨论】:

  • 我不断收到&lt;ws:consumer-config&gt; 的错误。把它放在我问题的最后一个“编辑”部分。
  • HTTP 侦听器是较新的 HTTP 连接器源。基于传输的 HTTP 连接器(入站/出站端点)在 Mule 3.6.0 (docs.mulesoft.com/release-notes/mule-runtime/…) 中已弃用。您必须使用任一连接器作为流源(流的开始),但不能在中间。解决此问题后,您可能需要进行一些转换。 Web 服务使用者可能不期望 CXF 对象作为输入,而是普通对象或 XML。
  • 不断收到consumer-config 的“xml 无效”。我添加了:(1) serviceport 元素的 name 属性,从 wsdlconsumer-config 的“服务”和“端口”。没用。然后尝试:(2)BService类(从wsdl生成,用“WebServiceClient”注释)和BServiceIntefrace类(从wsdl生成,端口类)。它们都不起作用。我相信这是你的榜样。您在这里的“服务”和“端口”中究竟放了什么:github.com/mulesoft/docs-connectors/blob/latest/…
猜你喜欢
  • 2012-12-25
  • 2015-03-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多