读书人

透过 CXF 使用 WS-Security

发布时间: 2012-10-12 10:17:04 作者: rapoo

通过 CXF 使用 WS-Security

与 本系列 前面的文章讨论的 Axis2 和 Metro Web 服务堆栈一样,Apache CXF 支持使用 WS-Security SOAP 扩展技术提供一整套用于消息交换的与安全相关的功能(关于 CXF 的基础知识见 “CXF 简介”)。与这些堆栈一样,CXF 也使用 WS-SecurityPolicy 配置 WS-Security 安全处理(也可以手工配置)。

CXF 的 WS-Security 实现基于开放源码的 WSS4J 库(见 参考资料)。Axis2 代码也使用这个库,因此这两个堆栈的 WS-Security 配置细节有一些相似之处。但是,通过解释 WS-SecurityPolicy 配置 WSS4J 的代码层不一样。在 Axis2 中这由单独发布的 Rampart 模块处理,而在 CXF 中由 cxf-rt-ws-policy 和 cxf-rt-ws-security 模块处理(这些模块包含在标准的 cxf-#.jar 中,其中的 # 是版本号)。

在本文中,您会看到在 CXF 中配置 WS-Security 处理的两个示例。第一个示例是一个简单的 UsernameToken,它仅仅包装明文用户名和密码。第二个示例使用 X.409 证书和密钥对消息进行签名和加密。这些示例与 “Axis2 WS-Security 基础” 和 “Axis2 WS-Security 签名和加密” 中通过 Axis2 和 Metro 实现的示例一致,这样您就可以看出这些堆栈的技术差异。可以 下载 本文的源代码。

?

<!-- Policy for UsernameToken with plaintext password, sent from client to server only --> <wsp:Policy wsu:Id="UsernameToken" xmlns:wsu= "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702"> <wsp:ExactlyOne> <wsp:All> <!-- Empty <TransportBinding/> element required due to bug in CXF 2.2.6 --> <sp:TransportBinding/> <sp:SupportingTokens> <wsp:Policy> <sp:UsernameToken sp:IncludeToken=".../IncludeToken/AlwaysToRecipient"/> </wsp:Policy> </sp:SupportingTokens> </wsp:All> </wsp:ExactlyOne> </wsp:Policy> <wsdl:types> ... </wsdl:types> <wsdl:message name="getBookRequest"> <wsdl:part element="wns:getBook" name="parameters"/> </wsdl:message> ... <wsdl:portType name="Library"> <wsdl:operation name="getBook"> <wsdl:input message="wns:getBookRequest" name="getBookRequest"/> <wsdl:output message="wns:getBookResponse" name="getBookResponse"/> </wsdl:operation> ... </wsdl:portType> <wsdl:binding name="LibrarySoapBinding" type="wns:Library"> <wsp:PolicyReference xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" URI="#UsernameToken"/> <wsdlsoap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/> <wsdl:operation name="getBook"> <wsdlsoap:operation soapAction="urn:getBook"/> <wsdl:input name="getBookRequest"> <wsdlsoap:body use="literal"/> </wsdl:input> <wsdl:output name="getBookResponse"> <wsdlsoap:body use="literal"/> </wsdl:output> </wsdl:operation> ... </wsdl:binding> <wsdl:service name="CXFLibrary"> <wsdl:port binding="wns:LibrarySoapBinding" name="library"> <wsdlsoap:address location="http://localhost:8080/cxf-library-username"/> </wsdl:port> </wsdl:service></wsdl:definitions>

?

在 清单 1 与 Axis2 和 Metro 示例使用的 WSDL 之间有一个重要的差异。这个版本的 WS-SecurityPolicy 配置中包含一个空的 <sp:TransportBinding/> 元素,这是因为本文使用的 CXF 2.2.6 中有一个 bug。如果没有 <sp:TransportBinding/> 或某种形式的加密或签名,CXF 的 WS-SecurityPolicy 处理就无法处理 UsernameToken。在高于 2.2.6 的 CXF 版本中应该会纠正这个错误。

清单 1 中的 WSDL 告诉要访问这个服务的任何客户机在安全处理方面需要做什么。正如前面提到的,要想使用策略,一般需要向 CXF 提供额外的参数。在这个示例中,参数是客户机代码在发送请求时使用的用户名和密码,还有服务器端在接收请求时检验用户名和密码的方法。下面的示例演示如何向消息交换的两端提供这些额外信息。

?

如果要使用固定的 WS-Security 参数值,静态配置方法比较方便。必须确保配置文件的名称及其在类路径中的位置正确,因为这个文件是可选的,如果没有找到它,CXF 会继续运行,不会发出警告(直到它试图使用 WS-Security 时由于缺少所需的参数而失败)。如果遇到了问题,可以检查客户机的 INFO 级日志输出。应该会看到消息 INFO: Loaded configuration file cxf.xml.(或者通过 cxf.config.file.url 系统属性设置的其他文件名);如果没有 看到这个消息,就说明没有找到文件,需要检查类路径以查明原因。

<jaxws:properties> <entry key="ws-security.callback-handler" value="com.sosnoski.ws.library.cxf.ServerCallback"/> </jaxws:properties> </jaxws:endpoint></beans>

?

为这个 UsernameToken 示例添加的配置信息是一个安全回调类。Axis2 和 Metro 示例也使用这种方法。WS-Security 代码用用户名和密码信息调用用户提供的回调类,这个类实现 javax.security.auth.callback.CallbackHandler 接口。这个回调类可以实现您需要的任何用户名和密码组合检验方法,所以这种技术非常灵活。

清单 5 给出示例代码使用的回调类。这个类可以处理检验用户名和密码的 UsernameToken 示例,也可以处理使用签名和加密的示例(在本文的下一节中讨论)。


清单 5. 服务器端回调类

<!-- Policy for first signing and then encrypting all messages, with the certificate included in the message from client to server but only a thumbprint on messages from the server to the client. --> <wsp:Policy wsu:Id="SignEncr" xmlns:wsu="http://docs.oasis-open.org/...-wss-wssecurity-utility-1.0.xsd" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"> <wsp:ExactlyOne> <wsp:All> <sp:AsymmetricBinding xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702"> <wsp:Policy> <sp:InitiatorToken> <wsp:Policy> <sp:X509Token sp:IncludeToken=".../AlwaysToRecipient"> <wsp:Policy> <sp:RequireThumbprintReference/> </wsp:Policy> </sp:X509Token> </wsp:Policy> </sp:InitiatorToken> <sp:RecipientToken> <wsp:Policy> <sp:X509Token sp:IncludeToken=".../Never"> <wsp:Policy> <sp:RequireThumbprintReference/> </wsp:Policy> </sp:X509Token> </wsp:Policy> </sp:RecipientToken> <sp:AlgorithmSuite> <wsp:Policy> <sp:TripleDesRsa15/> </wsp:Policy> </sp:AlgorithmSuite> <sp:Layout> <wsp:Policy> <sp:Strict/> </wsp:Policy> </sp:Layout> <sp:IncludeTimestamp/> <sp:OnlySignEntireHeadersAndBody/> </wsp:Policy> </sp:AsymmetricBinding> <sp:SignedParts xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702"> <sp:Body/> </sp:SignedParts> <sp:EncryptedParts xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702"> <sp:Body/> </sp:EncryptedParts> </wsp:All> </wsp:ExactlyOne> </wsp:Policy> <wsdl:types> ... </wsdl:types> <wsdl:message name="getBookRequest"> <wsdl:part element="wns:getBook" name="parameters"/> </wsdl:message> ... <wsdl:portType name="Library"> ... </wsdl:portType> <wsdl:binding name="LibrarySoapBinding" type="wns:Library"> <wsp:PolicyReference xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" URI="#SignEncr"/> <wsdlsoap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/> <wsdl:operation name="getBook"> <wsdlsoap:operation soapAction="urn:getBook"/> <wsdl:input name="getBookRequest"> <wsdlsoap:body use="literal"/> </wsdl:input> <wsdl:output name="getBookResponse"> <wsdlsoap:body use="literal"/> </wsdl:output> </wsdl:operation> ... </wsdl:binding> <wsdl:service name="CXFLibrary"> <wsdl:port binding="wns:LibrarySoapBinding" name="library"> <wsdlsoap:address location="http://localhost:8080/cxf-library-signencr"/> </wsdl:port> </wsdl:service></wsdl:definitions>

?

清单 6 与前面文章中使用的 WSDL 之间只有一个显著的差异,WS-Policy/WS-SecurityPolicy 部分转移到了 WSDL 的开头,与 WSDL 1.1 模式定义的最新版本保持一致。

与简单的 UsernameToken 示例相比,使用私有密钥-证书对进行消息签名和加密的配置比较复杂。需要指定密钥存储作为密钥和证书的来源,还要提供访问密钥存储所需的密码。必须通过 .properties 文件提供密钥存储信息;必须通过回调提供用于访问私有密钥的密码。接下来,看看在客户机和服务器上的实现方式。

?

清单 7 所示的 cxf-client.xml 定义了两对属性文件和用户名,一对用于签名处理,另一对用于加密处理。每个属性文件指定一个密钥存储并提供访问密钥存储所需的信息。相关联的用户名值指定存储中要使用的密钥(用于处理签名)或证书(用于处理加密)。在这里,签名处理和加密处理使用同一个密钥存储,其中包含服务器证书与客户机私有密钥和证书。因为只有一个存储,这两个属性引用同一个 client-crypto.properties 文件。这个文件必须放在类路径的根目录中,见 清单 8:


清单 8. client-crypto.properties 文件

<jaxws:properties> <entry key="ws-security.signature.properties" value="server-crypto.properties"/> <entry key="ws-security.signature.username" value="serverkey"/> <entry key="ws-security.encryption.username" value="useReqSigCert"/> <entry key="ws-security.callback-handler" value="com.sosnoski.ws.library.cxf.ServerCallback"/> </jaxws:properties> </jaxws:endpoint></beans>

?

这个服务器版本与客户机设置的主要差异是,它没有指定加密属性文件,而且加密用户名设置是 useReqSigCert。这个值是 WSS4J 能够识别的特殊名称,这表示应该使用请求签名所用的客户机证书对响应进行加密。使用这个设置让服务器代码可以处理多个客户机,每个客户机有自己的证书。

server-crypto.properties 文件实际上与 清单 8 所示的 client-crypto.properties 相同。服务器回调类与 UsernameToken 示例中使用的相同,见 清单 5。

描述名字大小下载方法本文的源代码j-jws13.zip28KBHTTP

?

原文:http://www.ibm.com/developerworks/cn/java/j-jws13.html

读书人网 >软件架构设计

热点推荐