web-services - 将 ws-security 添加到 Mule 中的流
问题描述
- 我正在使用没有任何工作室的骡子。
- mule 中暴露的 web 服务。客户端发送请求,我转换消息(fe 填写一些字段)并将其转发到外部 Web 服务(不由我管理)。
- 用于外部 Web 服务的 Java 类是使用 wsimport 基于它们的 wsdl 创建的
- 他们需要基于他们给我的密钥库的 wss xml 签名
- 他们的服务(和 wsdl)可以通过 http 访问
- 我的目标:我想添加 ws-security(
Signature
现在只是行动)以便能够向他们的 WS 发出请求。 - 总结:客户端向我拥有的 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
,进行创建signaturePropFile
,passwordCallbackRef
并以某种方式将其与 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):
- 骡子sdk
3.8.0
- 回答当我应用我的解决方案时会发生什么很复杂。我不确定应该将 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">
.
解决方案
如果可能的话,我会避免使用 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/
推荐阅读
- php - 我如何使用与 Doctrine 的关系?
- html - 如何在 CSS 中开发导航栏
- java - 如何在 Spring Boot 中将 mongo db update 运算符应用为 $inc
- javascript - Javascript如何等待点击功能完成然后在用户再次点击后才重复相同的功能?
- excel - 修剪函数的 Excel IF 语句
- c# - Microsoft.Odata.Odataexception:找到不平衡的括号表达式
- xamarin.forms - 检查语音通话的默认 sim 设置
- sql-server - 从 varbinary 获取位范围
- socket.io - Nifi 的 ConnectWebSocket 是否应该从 socket.io websocket 服务器获取消息?
- python - 如何使用线性回归预测值列表?