以下文章为 cxf 官方 google 翻译,英文好的朋友可以直接看原文:
http://cxf.apache.org/docs/ws-security.html
WS - Security提供了手段,以确保您的服务,如HTTPS传输级别的协议超出。 通过一些标准,如XML加密,并在WS - Security标准中定义的头,它可以让你:
目前,CXF的实现结合的WS - Security WSS4J 。 要使用的集成,你需要配置这些拦截器,并将它们添加到您的服务和/或客户。
WS - Security的公共/私人密钥加密技术的大量使用。 要真正了解如何配置的WS - Security,它是有用的 - 如果没有必要 - 要了解这些基础知识。 维基百科在此有一个极好的入门 ,但我们会尝试总结了相关的基础知识(此内容是维基百科的内容修改后的版本.. )
与公共密钥加密,用户有一对公钥和私钥。 这些都是用一个大素数和一个关键功能。
键是有关数学的,但不能从一个派生。 与这些键,我们可以对信息进行加密。 例如,如果Bob想将消息发送给Alice,他可以用她的公钥加密消息。 然后,Alice可以解密此消息使用她的私钥。 只有Alice可以解密此消息,因为她是用私钥只有一个。
信息也可以签署。 这使您可以确保消息的真实性。 如果Alice想将消息发送给Bob,Bob想确保它是从Alice,Alice可以使用自己的私钥来签名消息。 然后,Bob可以确认该消息是从Alice用她的公钥。
为了使内CXF的WS - Security的服务器或客户端,你需要成立WSS4J拦截。 您可以通过API独立的Web服务或通过Spring XML配置servlet的主办。 本节将提供一个概述如何做到这一点,以下各节将进入更详细的拦截器配置为特定的安全行动。
重要的是要注意:
在服务器端,你将要添加的拦截您的CXF的端点。 如果你使用的JAX - WS的API发布您的服务,你可以得到你这样的CXF的端点:
import org.apache.cxf.endpoint.Endpoint; import org.apache.cxf.jaxws.EndpointImpl; EndpointImpl jaxWsEndpoint = (EndpointImpl) Endpoint.publish("http://host/service", myServiceImpl); Endpoint cxfEndpoint = jaxWsEndpoint.getServer().getEndpoint();
如果您使用过的(JAXWS)ServerFactoryBean,你可以简单地通过服务器对象的访问:
import org.apache.cxf.endpoint.Endpoint; import org.apache.cxf.endpoint.Server; import org.apache.cxf.frontend.ServerFactoryBean; ServerFactoryBean factory = ...; ... Server server = factory.create(); Endpoint cxfEndpoint = server.getEndpoint();
在客户端,您可以得到一个参考CXF的端点使用ClientProxy帮手:
import org.apache.cxf.frontend.ClientProxy; ... GreeterService gs = new GreeterService(); Greeter greeter = gs.getGreeterPort(); ... org.apache.cxf.endpoint.Client client = ClientProxy.getClient(greeter); org.apache.cxf.endpoint.Endpoint cxfEndpoint = client.getEndpoint();
现在您可以添加拦截器:
import org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor; import org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor; ... Map<String,Object> inProps= new HashMap<String,Object>(); ... // how to configure the properties is outlined below; WSS4JInInterceptor wssIn = new WSS4JInInterceptor(inProps); cxfEndpoint.getInInterceptors().add(wssIn); Map<String,Object> outProps = new HashMap<String,Object>(); ... // how to configure the properties is outlined below; WSS4JOutInterceptor wssOut = new WSS4JOutInterceptor(outProps); cxfEndpoint.getOutInterceptors().add(wssOut);
如果你使用Spring来构建端点(例如,如Tomcat servlet容器中运行的Web服务),您可以轻松地完成您的bean定义,而不是使用上面。
<import resource="classpath:META-INF/cxf/cxf.xml" /> <import resource="classpath*:META-INF/cxf/cxf-extension-*.xml" /> <jaxws:endpoint id="myService" implementor="com.acme.MyServiceImpl" address="http://localhost:9001/MyService"> <bean id="myPasswordCallback" class="com.mycompany.webservice.ServerPasswordCallback"/> <jaxws:inInterceptors> <!-- SAAJ Interceptor needs to be explicitly declared only in CXF 2.0.x --> <bean class="org.apache.cxf.binding.soap.saaj.SAAJInInterceptor"/> <bean class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor"> <constructor-arg> <map> <entry key="action" value="UsernameToken"/> <entry key="passwordType" value="PasswordDigest"/> <entry key="signaturePropFile" value="..."/> <entry key="passwordCallbackRef"> <ref bean="myPasswordCallback"/> </entry> ... </map> </constructor-arg> </bean> </jaxws:inInterceptors> </jaxws:endpoint>
参数的构造元素以上(行动,signaturePropFile等)映射到WSS4J的文本字符串的输入键和值WSHandlerConstants 和WSConstants 您在下面的章节中看到相应WSHandlerConstants.XXXXX和WSConstants.XXXX的常量类。
因此,通过观察WSHandlerConstants,例如,你可以看到以下的WSHandlerConstants.USERNAME_TOKEN值将需要“的UsernameToken”,而不是在做Spring配置。
如果你想避免WSHandlerConstants.XXXXX WSConstants.XXXX常数的文字键,你也可以使用Spring UTIL命名空间,在Spring上下文中引用静态常量,如下图所示。
<beans ... xmlns:util="http://www.springframework.org/schema/util" ... xsi:schemaLocation=" ... http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"> ... <bean class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor"> <constructor-arg> <map> <entry value="UsernameToken"> <key> <util:constant static-field="org.apache.ws.security.handler.WSHandlerConstants.ACTION"/> </key> </entry> ... </map> </constructor-arg> </bean> ...
虽然CXF WSS4J拦截器支持标准配置属性WSHandlerConstants.XXXXX和WSConstants.XXXX,CXF还提供了一些额外的低水平WSS4J和其他一些安全相关的拦截器配置功能的访问。
2.2.8 CXF的,CryptoCoverageChecker拦截允许不迁移到一个基于WS - SecurityPolicy来配置一个验证签名和加密消息内容的覆盖面。 拦截器可以支持两种元素的含量水平的签名和加密覆盖的执法(知道签名和内容的结合,并不代表一个类型的覆盖面和覆盖范围的有效组合)。 要使用API配置这个拦截器,请按照下面的例子。
import org.apache.cxf.ws.security.wss4j.CryptoCoverageChecker; import org.apache.cxf.ws.security.wss4j.CryptoCoverageChecker.XPathExpression; import org.apache.cxf.ws.security.wss4j.CryptoCoverageUtil.CoverageScope; import org.apache.cxf.ws.security.wss4j.CryptoCoverageUtil.CoverageType; Map<String, String> prefixes = new HashMap<String, String>(); prefixes.put("ser", "http://www.sdj.pl"); prefixes.put("soap", "http://schemas.xmlsoap.org/soap/envelope/"); List<XPathExpression> xpaths = Arrays.asList( new XPathExpression("//ser:Header", CoverageType.SIGNED, CoverageScope.ELEMENT), new XPathExpression("//soap:Body", CoverageType.ENCRYPTED, CoverageScope.CONTENT)); CryptoCoverageChecker checker = new CryptoCoverageChecker(prefixes, xpaths);
拦截器也可以在使用常规的bean定义格式Spring配置。
上述配置后拦截,只需将您的客户端或服务器的拦截器链与WSS4J拦截previsouly显示拦截。 确保您包括WSS4JInInterceptor链中的所有请求将被拒绝,如果你执行任何覆盖的XPath。
CXF的2.0.10和2.1.4,您可以指定自定义WSS4J处理器配置的WSS4JInInterceptor。 要激活此配置选项,提供一个非WSS4J的产权界定,wss4j.processor.map,WSS4JInInterceptor在 Spring 的例子所示。
相同configuratoin可以通过API以及acheieved。 关键值是一个XML的WS - S的头元素的限定名称与给定的处理器实现过程。 条目的值可以是一个字符串,代表一个实例处理器,处理器,或者为null禁用处理给定的WS - S的头元素的对象的类名。
<bean class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor"> <constructor-arg> <map> ... <!-- This reconfigures the processor implementation that WSS4j uses to process a WS-S Signature element. --> <entry key="wss4j.processor.map"> <map key-type="javax.xml.namespace.QName"> <entry value="my.class"> <key> <bean class="javax.xml.namespace.QName"> <constructor-arg value="http://www.w3.org/2000/09/xmldsig#"/> <constructor-arg value="Signature"/> </bean> </key> </entry> </map> </entry> ... </map> </constructor-arg> </bean>
2.2.6 CXF的,您可以指定自定义WSS4J WSS4JOutInterceptor行动配置。 要激活此配置选项,提供一个非WSS4J的产权界定,wss4j.action.map,WSS4JOutInterceptor在次年春天的例子所示。
相同configuratoin可以通过API以及acheieved。 关键值是一个整数,代表WSS4J行动标识符。 条目的值可以是一个字符串,代表的行动,以实例的类名或对象实施行动。 此配置选项可以覆盖内置的行动实现,或添加您自己的。
<bean class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor"> <constructor-arg> <map> ... <!-- Redefines the action for SAMLTokenSigned to use a custom implementation. --> <entry key="wss4j.action.map"> <map key-type="java.lang.Integer" value-type="java.lang.Object"> <entry key="0x10" value-ref="mySamlTokenSignedAction"/> </map> </entry> ... </map> </constructor-arg> </bean>
WS - Security支持指定标记的方法很多。 其中之一是的UsernameToken头。 它是一种标准的方式传达到另一个端点的用户名和密码或密码消化。 一定要检讨绿洲UserNameToken配置文件规范 为重要的安全考虑,在使用UsernameTokens时。 注意规范的建议,为防范重放攻击,nonce的支持尚未得到落实CXF或WSS4J。
对于服务器端,你需要设置以下WSS4JInInterceptor属性(见上面 的代码示例):
inProps.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN); // Password type : plain text inProps.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT); // for hashed password use: //properties.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_DIGEST); // Callback used to retrieve password for given user. inProps.put(WSHandlerConstants.PW_CALLBACK_CLASS, ServerPasswordHandler.class.getName());
密码回调类,允许您检索检索给定用户的密码,这样的WS - Security可以判断,如果他们授权。 这里是一个小例子:
import java.io.IOException; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.UnsupportedCallbackException; import org.apache.ws.security.WSPasswordCallback; public class ServerPasswordCallback implements CallbackHandler { public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { WSPasswordCallback pc = (WSPasswordCallback) callbacks[0]; if (pc.getIdentifier().equals("joe")) { // set the password on the callback. This will be compared to the // password which was sent from the client. pc.setPassword("password"); } } }
请注意,包括CXF的2.3.x,一个纯文本的密码(或任何其他未知的密码类型)特殊情况的密码验证委托回调类 ,看到org.apache.ws.security。 processor.UsernameTokenProcessor#handleUsernameToken()方法的Javadoc WSS4J 项目。 在这种情况下,ServerPasswordCallback应类似以下:
public class ServerPasswordCallback implements CallbackHandler { public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { WSPasswordCallback pc = (WSPasswordCallback) callbacks[0]; if (pc.getIdentifer().equals("joe") { if (!pc.getPassword().equals("password")) { throw new IOException("wrong password"); } } } }
CXF的2.4起,回调处理程序提供的所有情况下的密码,并验证是在内部完成(但可配置)。 更多信息, 请 参见这里。
在客户端,您将要配置的WSS4J传出属性:
outProps.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN); // Specify our username outProps.put(WSHandlerConstants.USER, "joe"); // Password type : plain text outProps.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT); // for hashed password use: //properties.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_DIGEST); // Callback used to retrieve password for given user. outProps.put(WSHandlerConstants.PW_CALLBACK_CLASS, ClientPasswordHandler.class.getName());
再次,我们正在使用的密码回调,除了这个时间,而不是说明我们在服务器端的密码,我们指定的密码,我们要随消息一起发送。 这是我们没有我们的配置文件存储在我们的密码。
import java.io.IOException; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.UnsupportedCallbackException; import org.apache.ws.security.WSPasswordCallback; public class ClientPasswordCallback implements CallbackHandler { public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { WSPasswordCallback pc = (WSPasswordCallback) callbacks[0]; // set the password for our message. pc.setPassword("password"); } }
在多个用户使用不同的密码的情况下, 使用 WSPasswordCallback“getIdentifier()方法来获得当前的SOAP请求的用户名。
这里是一个例子 使用拦截器(使用UsernameToken的)的注释实施的WS - Security 。
X.509证书令牌简介( PDF )提供了另一种实现WS - Security的选项。 对于签名和加密操作,你需要创建一个公共和私人的关键,所涉及的实体。 您可以通过以下步骤为您的开发环境生成自签名的密钥对。 记住这些不会象Verisign的外部权威签署的,所以在生产中使用不恰当的。
1。 创建与给定的别名,像“myAlias”/“myAliasPassword”在密钥库密码私钥(由密码保护 安全原因)
keytool -genkey -alias myAlias -keypass myAliasPassword -keystore \ privatestore.jks -storepass keyStorePassword -dname "cn=myAlias" -keyalg RSA
别名,是一个简单的的方法来确定密钥对。 在这种情况下,我们使用的是RSA算法。
2。 自签署的证书(在生产环境中,这将是做一个像VeriSign这样的公司)。
keytool -selfcert -alias myAlias -keystore privatestore.jks \ -storepass keyStorePassword -keypass myAliasPassword
3。 从我们的私人密钥库中导出的公钥文件命名key.rsa
keytool -export -alias myAlias -file key.rsa -keystore privatestore.jks \ -storepass keyStorePassword
4。 公钥导入到新的keystore:
keytool -import
-alias myAlias -file key.rsa -keystore publicstore.jks \
-storepass keyStorePassword
所以,现在我们有两个密钥库包含我们的钥匙 - 公共(publicstore.jks)和私人(privatestore.jks)。 他们都有keystore密码keyStorePass(不建议用于生产,但发展确定)和别名设置到myAlias。
该文件key.rsa可以从文件系统中删除,因为它只是暂时的使用。 强烈建议,因为一个keystore受密码保护密钥库中存储键。
密钥生成一个更详细的说明可以在这里找到:
http://java.sun.com/javase/6/docs/technotes/tools/solaris/keytool.html
如何创建一个生产证书,可以在这里找到:
http://support.globalsign.net/en/objectsign/java.cfm
邮件签名是用来验证该消息只能从某个发件人的收件人,以及消息在传输过程中不改变。 特别的发件人加密一个用其私有密钥的消息消化(散列),并在收件人unencrypting用发件人的公钥的哈希,并重新计算,以确保消息是不是在传输过程中改变的消息摘要(即,消化计算值由发送者和接收者都是一样的)。 对于这一过程的发生,你必须确保客户端的公钥已经被导入到服务器的keystore使用keytool。
在客户端,我们传出的WS - Security属性看起来像这样(见上面 的代码示例):
outProps.put(WSHandlerConstants.ACTION, "Signature"); outProps.put(WSHandlerConstants.USER, "myAlias"); outProps.put(WSHandlerConstants.PW_CALLBACK_CLASS, ClientCallbackHandler.class.getName()); outProps.put(WSHandlerConstants.SIG_PROP_FILE, "client_sign.properties");
指定的用户客户端的密钥别名。 密码回调类是负责提供该密钥的密码。
提示
为X.509的支持,您将通常有多个动作,例如加密与签名。 对于这些情况,只是空间独立的行动,在ACTION属性如下:
outProps.put(WSHandlerConstants.ACTION WSHandlerConstants.TIMESTAMP +“”+ WSHandlerConstants.SIGNATURE +“”+ WSHandlerConstants.ENCRYPT);
或者,您可能空间单独的字符串文字,您会看到在上面的Spring配置(例如,“签名加密”)
我们client_sign.properties文件包含几个设置来配置WSS4J:
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 = keyStorePassword
在服务器端,我们需要来配置我们的传入WSS4J拦截,使用客户端的公钥来验证签名。
inProps.put(WSHandlerConstants.ACTION,“签字”); inProps.put(WSHandlerConstants.SIG_PROP_FILE,“server.properties”);
我们server_sign.properties文件包含几个设置来配置WSS4J:
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 = amex123 org.apache.ws.security.crypto.merlin.file = server_keystore.jks
加密涉及的发件人与收件人的公钥加密的消息,以确保,只有收件人可以读取消息(只有收件人有其自己的私人密钥,解密消息所必需的。)这要求发件人以有收件人的公钥在其密钥库。
用于加密的过程是非常相似,的确通常与上面的签名过程中结合。 我们的WS - Security的测试 样品提供了一个加密的请求和响应的例子, 还要检查这个 博客条目月底到年底更显示SOAP请求和响应的签名和加密的例子。
以下是本人参照的几个网址:
http://www.ibm.com/developerworks/cn/java/j-jws13.html?ca=drs-cn-0419
http://www.benmccann.com/dev-blog/apache-cxf-tutorial-ws-security-with-spring/