Apache CXF 策略异常(WS 安全)- 无法检测到安全配置

问题描述 投票:0回答:3

确实需要一些帮助来解决这个问题,因为我已经用头撞砖墙好几天了。

我已经使用 Apache CXF 实现了一个非常简单的 JAX-WS 服务,我正在尝试添加 WS-SecurityPolicies 来加密和签署消息正文,但是在客户端我收到以下异常:

org.apache.cxf.ws.policy.PolicyException: Security configuration could not be detected. Potential cause: Make sure jaxws:client element with name attribute value matching endpoint port is defined as well as a security.signature.properties element within it.
    at org.apache.cxf.ws.security.wss4j.policyhandlers.AbstractCommonBindingHandler.unassertPolicy(AbstractCommonBindingHandler.java:92)
    at org.apache.cxf.ws.security.wss4j.policyhandlers.AbstractBindingBuilder.getSignatureBuilder(AbstractBindingBuilder.java:1797)
    at org.apache.cxf.ws.security.wss4j.policyhandlers.AsymmetricBindingHandler.doSignature(AsymmetricBindingHandler.java:693)
    at org.apache.cxf.ws.security.wss4j.policyhandlers.AsymmetricBindingHandler.doSignBeforeEncrypt(AsymmetricBindingHandler.java:171)
    at org.apache.cxf.ws.security.wss4j.policyhandlers.AsymmetricBindingHandler.handleBinding(AsymmetricBindingHandler.java:110)
    at org.apache.cxf.ws.security.wss4j.PolicyBasedWSS4JOutInterceptor$PolicyBasedWSS4JOutInterceptorInternal.handleMessageInternal(PolicyBasedWSS4JOutInterceptor.java:190)
    at org.apache.cxf.ws.security.wss4j.PolicyBasedWSS4JOutInterceptor$PolicyBasedWSS4JOutInterceptorInternal.handleMessage(PolicyBasedWSS4JOutInterceptor.java:109)
    at org.apache.cxf.ws.security.wss4j.PolicyBasedWSS4JOutInterceptor$PolicyBasedWSS4JOutInterceptorInternal.handleMessage(PolicyBasedWSS4JOutInterceptor.java:96)
    at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:308)
    at org.apache.cxf.endpoint.ClientImpl.doInvoke(ClientImpl.java:514)
    at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:423)
    at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:324)
    at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:277)
    at org.apache.cxf.frontend.ClientProxy.invokeSync(ClientProxy.java:96)
    at org.apache.cxf.jaxws.JaxWsClientProxy.invoke(JaxWsClientProxy.java:139)

这是我的 applicationContext.xml 客户端:

<bean id="passwordCallback"
        class="com.example.payment.engine.soap.service.ClientPasswordCallback"></bean>

<jaxws:client
    name="{http://service.soap.engine.payment.example.com/}HelloWorldImplService"
    createdFromAPI="true" id="helloClient"
    serviceClass="com.example.payment.engine.soap.service.HelloWorld"
    address="http://localhost:8080/payment-engine/api/soap/hello">
    <jaxws:properties>
        <entry key="ws-security.callback-handler" value-ref="passwordCallback" />
        <entry key="ws-security.encryption.properties" value="crypto.properties" />
        <entry key="ws-security.signature.properties" value="crypto.properties" />
        <entry key="ws-security.encryption.username" value="server" />
    </jaxws:properties>
</jaxws:client>

这是客户端 crypto.properties

org.apache.ws.security.crypto.merlin.keystore.file=client-keystore.jks
org.apache.ws.security.crypto.merlin.keystore.password=client-pass
org.apache.ws.security.crypto.merlin.keystore.private.password=key-pass
org.apache.ws.security.crypto.merlin.keystore.alias=client

最后这是 WSDL:

<wsdl:definitions xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
    xmlns:tns="http://service.soap.engine.payment.example.com/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
    xmlns:ns1="http://schemas.xmlsoap.org/soap/http" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy"
    xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" name="HelloWorldImplService"
    targetNamespace="http://service.soap.engine.payment.example.com/">
    <wsdl:types>
        <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://service.soap.engine.payment.example.com/"
            elementFormDefault="unqualified" targetNamespace="http://service.soap.engine.payment.example.com/" version="1.0">
            <xs:element name="sayHello" type="tns:sayHello" />
            <xs:element name="sayHelloResponse" type="tns:sayHelloResponse" />
            <xs:complexType name="sayHello">
                <xs:sequence />
            </xs:complexType>
            <xs:complexType name="sayHelloResponse">
                <xs:sequence>
                    <xs:element minOccurs="0" name="return" type="xs:string" />
                </xs:sequence>
            </xs:complexType>
        </xs:schema>
    </wsdl:types>
    <wsdl:message name="sayHelloResponse">
        <wsdl:part element="tns:sayHelloResponse" name="parameters"></wsdl:part>
    </wsdl:message>
    <wsdl:message name="sayHello">
        <wsdl:part element="tns:sayHello" name="parameters"></wsdl:part>
    </wsdl:message>
    <wsdl:portType name="HelloWorld">
        <wsdl:operation name="sayHello">
            <wsdl:input message="tns:sayHello" name="sayHello"></wsdl:input>
            <wsdl:output message="tns:sayHelloResponse" name="sayHelloResponse"></wsdl:output>
        </wsdl:operation>
    </wsdl:portType>
    <wsdl:binding name="HelloWorldImplServiceSoapBinding" type="tns:HelloWorld">
    <wsp:PolicyReference URI="#HelloWorldBindingPolicy"/>
        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" />
        <wsdl:operation name="sayHello">
            <soap:operation soapAction="" style="document" />
            <wsdl:input name="sayHello">
                <wsp:PolicyReference URI="#HelloWorldInputBindingPolicy" />
                <soap:body use="literal" />
            </wsdl:input>
            <wsdl:output name="sayHelloResponse">
                <wsp:PolicyReference URI="#HelloWorldOutputBindingPolicy" />
                <soap:body use="literal" />
            </wsdl:output>
        </wsdl:operation>
    </wsdl:binding>
    <wsdl:service name="HelloWorldImplService">
        <wsdl:port binding="tns:HelloWorldImplServiceSoapBinding" name="HelloWorldImplPort">
            <soap:address location="http://localhost:8080/payment-engine/api/soap/hello" />
        </wsdl:port>
    </wsdl:service>
    <wsp:Policy wsu:Id="HelloWorldBindingPolicy">
        <wsp:ExactlyOne>
            <wsp:All>
                <sp:AsymmetricBinding>
                    <wsp:Policy>
                        <sp:InitiatorToken>
                            <wsp:Policy>
                                <sp:X509Token sp:IncludeToken="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/AlwaysToRecipient">
                                    <wsp:Policy>
                                        <sp:WssX509V3Token11 />
                                    </wsp:Policy>
                                </sp:X509Token>
                            </wsp:Policy>
                        </sp:InitiatorToken>
                        <sp:RecipientToken>
                            <wsp:Policy>
                                <sp:X509Token sp:IncludeToken="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/Never">
                                    <wsp:Policy>
                                        <sp:WssX509V3Token11 />
                                        <sp:RequireIssuerSerialReference />
                                    </wsp:Policy>
                                </sp:X509Token>
                            </wsp:Policy>
                        </sp:RecipientToken>
                        <sp:Layout>
                            <wsp:Policy>
                                <sp:Strict />
                            </wsp:Policy>
                        </sp:Layout>
                        <sp:IncludeTimestamp />
                        <sp:OnlySignEntireHeadersAndBody />
                        <sp:AlgorithmSuite>
                            <wsp:Policy>
                                <sp:Basic128 />
                            </wsp:Policy>
                        </sp:AlgorithmSuite>
                        <sp:EncryptSignature />
                    </wsp:Policy>
                </sp:AsymmetricBinding>
                <sp:Wss11>
                    <wsp:Policy>
                        <sp:MustSupportRefIssuerSerial />
                    </wsp:Policy>
                </sp:Wss11>
            </wsp:All>
        </wsp:ExactlyOne>
    </wsp:Policy>
    <wsp:Policy wsu:Id="HelloWorldInputBindingPolicy">
        <wsp:ExactlyOne>
            <wsp:All>
                <sp:EncryptedParts>
                    <sp:Body />
                </sp:EncryptedParts>
                <sp:SignedParts>
                    <sp:Body />
                </sp:SignedParts>
            </wsp:All>
        </wsp:ExactlyOne>
    </wsp:Policy>
    <wsp:Policy wsu:Id="HelloWorldOutputBindingPolicy">
        <wsp:ExactlyOne>
            <wsp:All>
                <sp:EncryptedParts>
                    <sp:Body />
                </sp:EncryptedParts>
                <sp:SignedParts>
                    <sp:Body />
                </sp:SignedParts>
            </wsp:All>
        </wsp:ExactlyOne>
    </wsp:Policy>
</wsdl:definitions>

任何帮助将不胜感激!

java soap cxf jax-ws ws-security
3个回答
4
投票

我花了几天时间解决 Apache CXF 3.1.4 的这个问题和其他问题。事实上,CXF 中的错误消息很少提供有关修复内容的真正线索。所涉及的问题非常典型:它讲述了未找到的名称,但没有提供查找的实际值,其次,这不是我的上下文中的问题。

我从基于 XML/Spring 的 CXF 总线/策略配置开始,在测试和生产中工作了一年多,就像:

SpringBusFactory bf = new SpringBusFactory();
URL busFile = MyClient.class.getResource("/wssec_policy.xml");
Bus bus = bf.createBus(busFile);
BusFactory.setDefaultBus(bus);

其中 wssec_policy.xml 包含 jaxws:client 定义,如下所示:

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:cxf="http://cxf.apache.org/core" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxws="http://cxf.apache.org/jaxws" xsi:schemaLocation="...etc...">
<bean id="logOutbound" class="MyLoggingOutInterceptor"/>
<bean id="logInbound" class="MyLoggingInInterceptor"/>
<cxf:bus>
    <cxf:outInterceptors><ref bean="logOutbound"/></cxf:outInterceptors>
    <cxf:inInterceptors><ref bean="dumpMessageInterceptor"/><ref bean="logInbound"/></cxf:inInterceptors>
    <cxf:features><cxf:logging/></cxf:features>
</cxf:bus>
<jaxws:client name="{http://some.domain/webservices}servicioSoap" createdFromAPI="true">
        <jaxws:properties>
            <entry key="security.encryption.properties" value="etc/Client_Encrypt.properties"/>
            <entry key="security.signature.properties" value="etc/Client_Sign.properties"/>
        </jaxws:properties>
    </jaxws:client>
</beans>

其中,加密和签名.properties 包含 WSS4J 属性

我的挑战只是让它在 JRE 8 上与 CXF 3.1.4、BouncyCastle 1.52、Spring 4.2.4 一起工作;哪个没有,首先敲击您引用的错误。

我在 POJO 客户端中组装了代码,并开始深入研究 CXF 源代码以查找真正的原因,并发现上述默认总线设置(之前确实有效)不再向 WSS4J 提供 jaxws:client 元数据层。没有数据,因此出现错误。 Apache CXF 提供三种配置 WS-* 堆栈的方法(显式拦截器、基于 XML 的策略以及通过 Java API 基于策略)。 为了解决您的问题,我通过 java API 切换到基于策略的配置,如下所示:

serviceGDS = new WSDL2Java_generated_Service();
portGDS = serviceGDS.getServiceGdsSoap();
Map<String, Object> ctx = ((javax.xml.ws.BindingProvider)portGDS).getRequestContext();
Properties encProps = new Properties();
encProps.load(new FileInputStream("etc/Client_Encrypt.properties"));
ctx.put("security.encryption.properties", encProps);
Properties signProps = new Properties();
signProps.load(new FileInputStream("etc/Client_Sign.properties"));
ctx.put("security.signature.properties", signProps);
// setting the props below once helped solving the error at stake
ctx.put("security.signature.username","test_keypair");
ctx.put("security.encryption.username","gds_test_cert");

最后两个属性曾经需要进行调试,解决由于缺少密钥别名而导致的密钥长度错误错误!但最后,我注意到没有它们它又恢复了! (它们实际上在 Client_Encrypt 和 Client_Sign 属性中复制了密钥库别名)。

然后,我通过抑制 Client_Encrypt 和 Client_Sign 属性中的“org.apache.ws.security.crypto.merlin.keystore.provider=BC”解决了未找到加密证书错误(但在过去的版本中有效)。

我通过从 pkcs12 密钥库类型切换到 jks 解决了 丢失证书 错误。我发现 keystore.load() 不会获取所有包含的证书...使用相同的 p12 密钥库,根据调用它的上下文,我会得到不同的结果...确实有问题。

通过观察在实时服务器(在我的例子中是 JBoss EAP7)中的重新部署从未起作用,我解决了不支持非对称绑定,但是当部署的应用程序与服务器一起启动时(因此在服务器重新启动后),它就起作用了。我强烈怀疑类加载器问题。

让我补充一下,我曾经切换回 JRE 7(旧版运行正常),并且错误明显不同(我怀疑 JRE 中的策略配置文件)。

另一个主要困难是,所有错误,无论是处理请求甚至响应时的客户端异常,还是服务器端错误,都被 CXF 包装为带有简洁错误消息的肥皂错误,这使得很难理解真正的错误正在发生。让我取得进展的唯一方法是在 POJO 中组装所需的客户端代码,并在 CXF 和 WSS4J 源代码库的帮助下对其进行调试(当您使用调试器并向下钻取时,Eclipse/Maven 会为您获取源代码...高效!)


0
投票

设法通过使用以下方法使这一半工作

    HelloWorldImplService service = new HelloWorldImplService();
    HelloWorld port = service.getHelloWorldImplPort();

    Map<String, Object> ctx = ((BindingProvider) port).getRequestContext();
    ctx.put("ws-security.callback-handler", "com.example.payment.engine.soap.service.ClientPasswordCallback");
    ctx.put("ws-security.encryption.properties", "config/crypto.properties");
    ctx.put("ws-security.signature.properties", "config/crypto.properties");
    ctx.put("ws-security.encryption.username", "server");

    System.out.println(port.sayHello());

0
投票

在我的例子中,它没有获取放置在交互器中的属性,有必要以这种方式将它们直接添加到客户端:

Map<String, Object> outProps = new HashMap<>();        
outProps.put("action", "Timestamp Signature");
outProps.put("ws-security.signature.username", "userName");
outProps.put("ws-security.callback-handler", co.com.test.TtPasswordCallback.class);
outProps.put("ws-security.signature.properties", "client-sign.properties");    

DispatcherServiceService service = new DispatcherServiceService();
DispatcherService port = service.getDispatcherServicePort();
Client client = ClientProxy.getClient(port);
client.getRequestContext().put(Message.ENDPOINT_ADDRESS, "http://service...");
client.getRequestContext().putAll(outProps);
    
© www.soinside.com 2019 - 2024. All rights reserved.