最近公司要把web services传输内容全加密用到wss4j,于是上网看了篇帖子照着做没有跑通,于是做了下修改,如下: 一、wss4j简介Wss4j是apache开发的,标准实现WS-Security(WebService安全)的开源项目,它提供了用户名令牌环验证(UsernameToken)和传递消息时保证信息的完整性和真实性等一些WebService安全保障。 二、环境准备2.1开发环境准备 在正式开始前还要去apche网站下载一个rampart-1.5.mar 把这个东东放到WEB-INF/modules下去 三、用KEYTOOL生成一对JKS文件首先我们用keytool生成一对JKS文件, service.jks和client.jks。 service.jks的私钥和公钥keystore的密码是apache (注如果不会用keytool请自己看相关资料,我用的是apache提供sample的文件)
生成方法如下: C:/Documents and Settings/Administrator>keytool -genkey -keyalg RSA -keysize 512 C:/Documents and Settings/Administrator>keytool -genkey -keyalg RSA -keysize 512
2.2搭建webservice环境 将axis2.war包拷贝到tomcat安装目录下的webapps目录下。 启动Tomcat(D:/Tomcat5.5/bin/startup.bat),打开浏览器输入并访问:http://127.0.0.1:8080/axis2 来查看,结果如下图,表示axis2已经工作正常。 四、建立web应用 4.1 编写服务器端代码 首先简单介绍我的Wss4j实现WS-Security功能,很简单就是客户端发送一个字符串,服务器端得到该字符串,同时把字符串在发送给客户端,首先自己建立一个web应用工程, 这里就以我的wsc应用工程为例 在src下建一个包com.neusoft.wss4j.rempart.demo.services 在这里写一个类SimpleService作为服务器端 该类的内容是: package com.neusoft.wss4j.rempart.demo.services; publicclass SimpleService { public String echo(String arg) { return arg; } } 这个类的作用就是接收客户端的字符串,并且把该字符串返回给客户端。 这里还有个类,该类是实现UsernameToken和传送信息的安全性和完整性的核心,该类被配置在axis2.xml和service.xml中,从而能得到用户配置的axis2.xml中的信息,和服务器端配置的service.xml的信息。每当客户端发送请求时,它都要首先通过该类获得访问服务端的权限和获得发送数据所需要的加密密码,然后把数据加密发送给服务器端,如果没有权限则不能把数据发送到服务器端,每当服务器端想要把数据传送到客户端时,也要经过次类获得发送数据所需要的加密密码,然后把数据加密返回给客户端,客户端通过解密获得明文信息。它的内容如下: package com.neusoft.wss4j.rempart.demo.services; import org.apache.ws.security.WSPasswordCallback; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.UnsupportedCallbackException; import java.io.IOException; public class PWCBHandler 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.getIdentifer(); if("client".equals(id)) { pwcb.setPassword("apache"); } else if("service".equals(id)) { pwcb.setPassword("apache"); } else { throw new UnsupportedCallbackException(callbacks[i], "对不起,您不是授权用户,不能访问该WEB服务!"); } } } }
4.2 编写服务器端的描述文件services.xml 然后写一个解析该服务器类services.xml文件该文件的内容如下: <?xml version="1.0" encoding="UTF-8"?> <service name="wsc"> <operation name="echo"> <messageReceiver class="org.apache.axis2.rpc.receivers.RPCMessageReceiver"/> </operation> <parameter name="ServiceClass" locked="false"> com.neusoft.wss4j.rempart.demo.services.SimpleService </parameter> <module ref="rampart" /> <parameter name="InflowSecurity"> <action> <items>Timestamp Signature</items> <signaturePropFile> keys/service.properties </signaturePropFile> </action> </parameter> <parameter name="OutflowSecurity"> <action> <items>Timestamp Signature</items> <user>service</user> <passwordCallbackClass> com.neusoft.wss4j.rempart.demo.services.PWCBHandler </passwordCallbackClass> <signaturePropFile> keys/service.properties </signaturePropFile> <signatureKeyIdentifier> DirectReference </signatureKeyIdentifier> </action> </parameter> </service>
服务器wsc中有几个方法就需要配置几个<operation></operation> echo为wsc服务器类中的方法。wsc为服务的名字也就是后边的打包服务器端wsc.aar的名字。着重看下红色和粉色字体部分,红色这部分是客户端传来信息用数字签名来解密客户端传过来的加密信息本例通过keys文件夹下的service.properties这个文件找到service.jks对信息进行解密,粉色部分是服务器端把输出向客户端的信息加密用的,本例通过keys文件夹下的service.properties这个文件找到service.jks对信息加密的。 service.properties的内容如下: 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=apache org.apache.ws.security.crypto.merlin.file=keys/service.jks 写明了加密文件的类型,文件密码,文件名称。
4.3 生成.aar服务包 下边就可以根据一个服务器类SimpleService一个service.xml打包生成一个wsc.aar做为服务器端的程序。首先将这个SimpleService类打包,然后把service.xml放在打包后的MATE-INFO下边 这样服务器端程序wsc.aar就完成了。下边把wsc.aar copy到D:/program/Tomcat6.0/webapps/axis2/WEB-INF/services目录下(这里是以我的机器做为例子的) 然后重启tomcat输入http://127.0.0.1:8080/axis2/services/listServices 就可以看到我们部署到服务器上的服务了。
4.4 编写模拟第三方测试程序 下面我写一个模拟第三方的程序调用webservice的一个例子 在包com.neusoft.wss4j.rempart.demo.client中的Client 它的内容如下: package com.neusoft.wss4j.rempart.demo.client; import java.io.Reader; import java.io.StringReader; import org.apache.axiom.om.OMAbstractFactory; import org.apache.axiom.om.OMElement; import org.apache.axiom.om.OMFactory; import org.apache.axiom.om.OMNamespace; import org.apache.axis2.addressing.EndpointReference; import org.apache.axis2.client.Options; import org.apache.axis2.client.ServiceClient; import org.apache.axis2.context.ConfigurationContext; import org.apache.axis2.context.ConfigurationContextFactory; import org.jdom.Document; import org.jdom.Element; import org.jdom.input.SAXBuilder; public class Client { public static void main(String[] args) throws Exception { ConfigurationContext ctx = ConfigurationContextFactory .createConfigurationContextFromFileSystem( "D:/eclipse3.2/workspace/wsc/WebRoot/WEB-INF", "D:/eclipse3.2/workspace/wsc/WebRoot/WEB-INF/conf/axis2.xml"); ServiceClient client = new ServiceClient(ctx, null); Options options = new Options(); options.setAction("urn:echo"); options.setTo(new EndpointReference( "http://localhost:8080/wsc/services/wsc")); client.setOptions(options); OMElement response = client.sendReceive(getPayload("(*^__^*) 嘻嘻……")); OMElement element = response.getFirstElement(); //把返回的OMElement对象转换为 xml数据 SAXBuilder builder = new SAXBuilder(); Reader in = new StringReader(element.toString()); Document doc = null; try { doc = builder.build(in); Element Element = doc.getRootElement(); String aa = Element.getTextTrim(); System.out.println(aa); } catch (Exception e) { System.out.println(e.getMessage()); } } private static OMElement getPayload(String value) { OMFactory factory = OMAbstractFactory.getOMFactory(); OMNamespace ns = factory.createOMNamespace( http://services.demo.rempart.wss4j.neusoft.com);","ns1 OMElement elem = factory.createOMElement("echo", ns); OMElement childElem = factory.createOMElement("param0", null); childElem.setText(value); elem.addChild(childElem); return elem; } } 这个测试类就不多说了,粉色的部分是需要注意的地方。 还有个客户端的axis2.xml需要说明一下 他的主要内容如下只要把这部分粘贴到原来的axis2.xml即可: <module ref="rampart" /> <parameter name="InflowSecurity"> 注意一下红色的部分他是当客户端向服务器端发送数据时,首先访问com.neusoft.wss4j.rempart.demo.services.PWCBHandler这个类,得到访问权限和加密信息的文件密码,然后通过加密信息的文件密码和keys/client.properties文件找到加密需要的文件client.jks把信息加密发送给服务器端,粉色部分是通过keys/client.properties文件找到解密需要的文件client.jks来解密服务器端返回的加密信息。 Keys文件下的client.properties内容如下: 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=apache org.apache.ws.security.crypto.merlin.file=keys/client.jks
五、总 结 整理一下思路 1 客户端发送消息给服务器端:如果客户端想请求服务器端首先读取客户端配置文件axis2.xml文件,得到访问的用户<user>client</user>然后找到com.neusoft.wss4j.rempart.demo.services.PWCBHandler类,看用户是否有访问服务的权限,如果有则把client.jks文件的密码给用户client,client通过密码在axis2.xml文件中找到<signaturePropFile>keys/client.properties</signaturePropFile>找到client.properties文件,在client.properties文件中找到client.jks文件,使用该文件的client私钥从而实现把传送的信息加密,然后把加密的信息发送到服务器端。 2 服务器端接收客户端发送来的消息:服务器端接收到消息,然后读取service.xml文件找到<signaturePropFile>keys/service.properties</signaturePropFile>从而找到service.properties文件,通过该文件找到service.jks文件使用该文件的client的公钥 解密客户端传送来的信息。 3 服务器端返回信息给客户端:获得客户端传送过来的明文信息后,从service.xml文件 得到加密的用户<user>service</user>通过 <passwordCallbackClass> com.neusoft.wss4j.rempart.demo.services.PWCBHandler </passwordCallbackClass> 找到验证类PWCBHandler得到加密需要的service.jks的加密密码apache 通过<signaturePropFile> keys/service.properties </signaturePropFile>找到service.properties文件,通过该文件找到service.jks文件,通过该文件的service的私钥把需要发送给客户端的信息加密。然后发送给客户端 4 客户端接收服务器端返回的消息:客户端端接收到消息,然后读取axis2.xml文件找到<signaturePropFile>keys/service.properties</signaturePropFile>从而找到client.properties文件,通过该文件找到client.jks文件使用该文件的service的公钥 解密服务器端返回来的信息。 |