像本系列前面的文章中讨论的Axis2和Metro Web服务堆栈一样,Apache CXF(您在“ CXF简介 ”中遇到了)支持使用WS-Security SOAP扩展技术来为消息提供全方位的与安全性相关的功能。交流。 与这些其他堆栈类似,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以及“ WS-Security with Metro ”中的Metro所使用的示例相匹配,因此您可以比较技术以查看堆栈之间的差异。 请参阅下载以获取本文的示例代码。
WS-SecurityPolicy安全配置详细说明了在客户端和服务之间交换消息所需的安全处理。 在大多数情况下,Web服务堆栈还需要一些其他信息才能将安全性应用于消息交换。 例如,WS-SecurityPolicy可能要求客户端对发送到服务器的请求消息进行签名,从而为服务提供不可否认性。 在这种情况下,客户端Web服务堆栈需要某种方式来标识在向服务发送消息时用于签名的特定私钥。
Axis2和Metro都使用自定义WS-SecurityPolicy扩展来提供这种类型的安全参数。 由于WS-SecurityPolicy通常嵌入在WSDL服务描述中,因此通常需要修改WSDL文档以添加这些详细信息(尽管Axis2允许您直接在客户端代码中设置策略,作为替代方法)。 修改WSDL文档的需求既麻烦又有点违背WSDL的意图,后者旨在充当服务描述。
CXF采用不同的方法-或应该采用不同的方法 -因为在将WS-SecurityPolicy配置应用于消息时,有多种方法可以使用所需的添加参数来配置CXF。 在客户端,您可以直接在客户端代码中或通过使用Spring XML配置文件来执行此操作。 在服务器端,尽管仍然可以在不同类型的文件中进行选择,但始终需要使用XML配置文件。 您将在本文的示例中看到客户端和服务器的这些替代方案如何工作。
UsernameToken
UsernameToken
提供了一种使用WS-Security表示用户名和密码对的标准方法。 密码信息可以作为纯文本(通常仅在与传输层安全性[TLS]或WS-Security加密结合使用时用于生产,但便于测试)发送或作为哈希值发送。 除了对许多需要直接认证的应用程序有用之外, UsernameToken
是WS-Security功能的最简单形式,并且为示例提供了一个很好的起点。
要在CXF上实现一个简单的纯文本UsernameToken
示例,您需要一个WSDL服务定义,其中包括适当的WS-Policy / WS-SecurityPolicy配置。 清单1显示了“ CXF简介 ”中使用的基本WSDL服务定义的编辑版本。 清单1包括策略信息,要求在从客户端到服务器的请求上要求UsernameToken
。
的策略引用以粗体显示,策略本身也以粗体显示。
UsernameToken
WSDL
...
...
...
...
清单1 WSDL与Axis2和Metro示例中用于UsernameToken
WSDL之间存在一个显着差异。 此版本包含一个空的
元素作为WS-SecurityPolicy的一部分,由于本文使用的CXF 2.2.6发行版中存在错误,因此有必要。 没有
或某种形式的加密或签名,CXF的WS-SecurityPolicy处理无法处理UsernameToken
。 在2.2.6之后的CXF版本中,应更正此错误。
清单1的 WSDL告诉想要访问该服务的任何人在安全性方面需要做什么 。 如前所述,要使用策略,通常需要向CXF提供其他参数。 在这种情况下,这些参数是发送请求时要使用的客户端代码的用户名和密码,以及在接收请求时在服务器端验证用户名和密码的方法。 接下来,我将向您展示如何为交易所的每一方提供此附加信息的示例。
配置CXF客户端WS-Security支持可以在客户端代码中动态处理,也可以在配置文件中静态处理。 清单2显示了客户端代码中UsernameToken
动态配置的示例:
UsernameToken
动态配置 // create the client stub
CXFLibrary service = new CXFLibrary();
Library stub = service.getLibrary();
...
// set the username and password
Map ctx = ((BindingProvider)stub).getRequestContext();
ctx.put("ws-security.username", "libuser");
ctx.put("ws-security.password", "books");
JAX-WS客户端使用生成的代理接口来访问服务。 在清单2的代码中,这是Library
接口。 通过调用生成的javax.xml.ws.Service
子类(在本例中为CXFService
类)中的方法,可以创建接口的实例(在示例代码中称为stub )。 尽管它没有反映在生成的代码的API中,但是JAX-WS保证返回的代理接口实例始终是javax.xml.ws.BindingProvider
类的子类。 要动态配置CXF,您需要利用这种隐式类型并将代理转换为BindingProvider
类,然后通过该转换访问请求上下文属性映射。 清单2显示了如何在此属性映射中设置用于WS-Security处理的用户名和密码。
静态配置使用与动态配置相同的属性值,只是以不同的方式进行设置。 CXF在启动时会在类路径中检查配置文件,如果找到该文件,则使用它来设置属性值。 默认情况下,配置文件必须命名为cxf.xml且位于类路径的根目录中(尽管您可以使用系统属性cxf.config.file.url
更改此默认值)。 清单3展示了一个cxf.xml文件的示例(在下载代码中以cxf-username-client.xml的形式出现),可以用来代替清单2所示的动态配置:
UsernameToken
静态配置
当您为WS-Security参数使用固定值时,静态配置方法会很方便。 您确实需要注意配置文件的名称和在类路径中的放置,因为该文件是可选的,并且如果找不到该文件,CXF将在无投诉的情况下运行(直到尝试在没有必需参数的情况下使用WS-Security时失败) 。 如果确实遇到问题,则可以检查客户端的INFO-level
日志记录输出。 您应该看到一条信息INFO: Loaded configuration file cxf.xml.
(或使用cxf.config.file.url
系统属性设置的其他文件名); 如果没有看到该消息,则说明找不到该文件,因此需要查找类路径以查找原因。
在服务器端,必须将配置文件用于WS-Security参数。 最简单的方法是将信息添加到定义服务端点的cxf-servlet.xml文件中。 清单4显示了“ CXF简介 ”中使用的cxf-servlet.xml的修改版本,并以粗体显示了添加的WS-Security信息(在下载代码中以server / etc / cxf-username-servlet.xml的形式出现) :
此UsernameToken
案例添加的配置信息只是一个安全回调类。 这与Axis2和Metro示例中使用的方法相同,其中WS-Security代码调用用户提供的回调类,以用户名和密码信息实现javax.security.auth.callback.CallbackHandler
接口。 回调类可以实现您要用来验证用户名和密码的组合的任何类型的处理,因此它是一种允许最大灵活性的技术。
清单5显示了示例代码使用的回调类。 此类包括处理UsernameToken
验证用户名和密码的情况以及使用签名和加密的情况(在本文的下一个主要部分中讨论)。
**
* Simple password callback handler. This just handles two cases: matching the username
* and password, and providing the password used for access to the private key.
*/
public class ServerCallback implements CallbackHandler {
public void handle(Callback[] callbacks)
throws IOException, UnsupportedCallbackException {
for (int i = 0; i < callbacks.length; i++) {
WSPasswordCallback pwcb = (WSPasswordCallback)callbacks[i];
String id = pwcb.getIdentifier();
switch (pwcb.getUsage()) {
case WSPasswordCallback.USERNAME_TOKEN_UNKNOWN:
// used when plaintext password in message
if (!"libuser".equals(id) || !"books".equals(pwcb.getPassword())) {
throw new UnsupportedCallbackException(callbacks[i], "check failed");
}
break;
case WSPasswordCallback.DECRYPT:
case WSPasswordCallback.SIGNATURE:
// used to retrieve password for private key
if ("serverkey".equals(id)) {
pwcb.setPassword("serverpass");
}
break;
}
}
}
}
作为使用默认cxf-servlet.xml的替代方法,可以在Web应用程序的web.xml文件中为服务器端配置配置其他文件。 由于必须直接指定要在服务器上使用的每个CXF模块,因此该方法的实现要稍微复杂一些,但是它确实允许更快地启动Web应用程序。 有关详细信息,请参见CXF文档“配置”页面,位于“服务器配置文件”主题下。
在尝试示例代码之前,您需要在系统上下载并安装最新版本的CXF(请参阅参考资料 )。 您还需要在解压缩的样本代码下载的根目录中编辑build.properties文件,以将cxf-home
属性的值更改为CXF安装的路径。 如果要在其他系统或端口上的服务器上进行测试,则可能还需要更改host-name
和host-port
。
要使用提供的Ant build.xml构建示例应用程序,请打开控制台到下载代码的根目录,然后键入ant
。 这将首先调用CXF WSDLToJava
工具来生成JAX-WS 2.x服务类和JAXB 2.x数据模型类,然后编译客户端和服务器,最后将服务器代码打包为WAR。 然后,您可以将生成的cxf-library-username.war文件部署到测试服务器,最后在控制台上键入ant run
来尝试运行示例客户端。 样本客户端通过对服务器的一系列请求序列,为每个请求打印简要结果。
UsernameToken
的简单性使其成为一个很好的起点,但这并不是WS-Security的典型用法。 当您使用WS-Security时,通常会涉及签名或加密,或者两者都涉及。 清单6显示了同时使用签名和加密的WSDL的经过编辑的示例(基于“ 带有Metro的WS-Security ”和早期的“ Axis2 WS-Security签名和加密 ”中的示例)。有关的更多详细讨论,请参见Axis2文章。一般进行签名和加密,以及为WS-Security生成和使用自签名证书的详细信息)。 WSDL的策略部分以粗体显示。
...
...
...
...
清单6 WSDL与早期文章中使用的唯一区别是,WS-Policy / WS-SecurityPolicy部分已移至WSDL的开头,与WSDL 1.1模式定义的最新版本保持一致。
与简单的UsernameToken
示例相比,使用私钥证书对进行签名和加密消息的配置更为复杂。 您需要将密钥存储区标识为密钥和证书的来源,还需要提供从存储区访问密钥所需的密码。 密钥库信息必须由.properties文件提供; 用于访问私钥的密码必须由回调提供。 接下来,您将看到它对于客户端和服务器如何工作。
就像在UsernameToken
示例中一样,您可以直接在客户端代码中或使用cxf-client.xml配置文件来配置对邮件进行签名和加密所需的安全性参数。 清单7显示了用于此目的的cxf-client.xml(下载示例代码中的cxf-signencr-client.xml):
清单7 cxf-client.xml定义了两对属性文件和用户名,一对用于签名处理,另一对用于加密处理。 每个属性文件都标识一个密钥存储区,并提供该存储区的访问信息。 关联的用户名值标识该商店中要用于处理的密钥(用于签名)或证书(用于加密)。 在这种情况下,签名处理和加密处理使用相同的密钥存储,该密钥存储同时包含服务器证书以及客户端私钥和证书。 由于只有一个商店,所以两个属性都引用相同的client-crypto.properties文件。 清单8中显示了该文件,该文件必须存在于类路径的根目录中:
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=nosecret
org.apache.ws.security.crypto.merlin.file=client.keystore
清单8的属性文件由基础WSS4J WS-Security代码用来配置签名和加密处理。 它标识用于处理签名和加密处理的“提供者”,密钥库的类型,密钥库密码和密钥库文件(必须存在于类路径的根目录中)。
除了密钥存储信息之外, 清单7的 cxf-client.xml文件还定义了另一个参数ws-security.callback-handler
,该参数先前在清单4的 cxf-servlet.xml中可见。 与前面的示例一样,此参数的值必须是安全回调处理程序类。 当WSS4J代码需要访问用于保护密钥存储区中的客户端私钥的密码时,它将调用此类的实例。 清单9中显示了示例代码中使用的实现:
/**
* Simple password callback handler. This just checks if the password for the private key
* is being requested, and if so sets that value.
*/
public class ClientCallback implements CallbackHandler {
public void handle(Callback[] callbacks) throws IOException {
for (int i = 0; i < callbacks.length; i++) {
WSPasswordCallback pwcb = (WSPasswordCallback)callbacks[i];
String id = pwcb.getIdentifier();
int usage = pwcb.getUsage();
if (usage == WSPasswordCallback.DECRYPT || usage == WSPasswordCallback.SIGNATURE) {
// used to retrieve password for private key
if ("clientkey".equals(id)) {
pwcb.setPassword("clientpass");
}
}
}
}
}
就像在UsernameToken
示例中一样,您可以在客户端代码中配置安全性参数,以替代使用cxf-client.xml文件。 您甚至可以用在代码中构造的值替换清单8的属性文件,将java.util.Properties
设置为请求上下文中ws-security.encryption.properties
键的值。 (请参见清单2的示例,该示例在上下文中设置用户名和密码属性。)
在服务器端,您需要在cxf-servlet.xml文件中包括与为客户端提供的安全性基本相同的安全性参数。 清单10显示了示例代码中使用的修改后的cxf-servlet.xml(您可以在其中找到它作为server / etc / cxf-signencr-servlet.xml),并以粗体显示添加的WS-Security参数:
与客户端设置的主要区别在于此服务器版本未指定加密属性文件,并且加密用户名设置为useReqSigCert
。 该值是WSS4J识别的特殊名称,表示用于签署请求的客户端证书应用于加密响应。 使用此设置,服务器代码可以与多个客户端一起使用,每个客户端都有自己的证书。
server-crypto.properties文件与清单8所示的client-crypto.properties本质上相同。 服务器回调类与UsernameToken
示例相同,如清单5所示。
对于签名和加密示例,您需要将build.properties文件更改为使用variant-name=signencr
(而不是UsernameToken
示例的username
值)。 除此之外,您遵循与 UsernameToken
示例相同的构建步骤 。
如果使用当前的2.2.6版本的CXF运行客户端,则会看到一些WARNING
级别的日志记录输出,例如WARNING: No assertion builder for type ... registered
。 这些消息并不表示代码中有任何问题,并且可能会在更高版本的CXF中消除。
在本文中,您已经了解了如何在CXF中使用WS-Security。 像Axis2和Metro一样,CXF在WSDL中支持WS-SecurityPolicy作为WS-Security配置的标准方法。 根据您的应用程序需求,您可以通过几种方式配置其他必需的安全性参数,而无需在服务WSDL中嵌入部署信息。 在这方面,与Axis2和Metro相比,CXF更容易,更干净地用于WS-Security。
测试本文的示例代码显示CXF中的一个错误,该错误已得到修复。 除非该策略还要求某种其他形式的安全处理,否则该错误将导致UsernamePolicy
被忽略。 很难根据本文中使用的简单示例来判断CXF WS-SecurityPolicy处理的健壮性,但是这种设计似乎是合理的,而且随着越来越多的人使用CXF的这一相对较新的功能,实现中的任何古怪之处也很可能将很快得到解决。
Java Web服务的下一部分将与CXF一起继续,这次着眼于性能。 查看CXF性能与最新的Axis2和Metro版本相比如何进行比较,以便进行简单的消息交换和使用WS-Security。
翻译自: https://www.ibm.com/developerworks/java/library/j-jws13/index.html