用axis发布webservices(六)
2007-01-17 08:54 P.M.
签名、加密、解密、身份验证的实现
对SOAP消息的签名、加密、解密、身份验证都放在一个名为WSSHelper的类中进行。
例程7 签名、加密、解密、身份验证功能的实现――WSSHelper.java
package com.hellking.study.webservice;
import com.verisign.messaging.WSSecurity;
...
public class WSSHelper {
static String PROVIDER="ISNetworks";//JSSE安全提供者。
//添加JSSE安全提供者,你也可以使用其它安全提供者。只要支持DESede算法。
static
{
java.security.Security.addProvider(new com.isnetworks.provider.jce.ISNetworksProvider());
}
/**
*对XML文档进行数字签名。
*/
public static void sign(Document doc, String keystore, String storetype,
String storepass, String alias, String keypass) throws Exception {
FileInputStream fileInputStream = new FileInputStream(keystore);
java.security.KeyStore keyStore = java.security.KeyStore.getInstance(storetype);
keyStore.load(fileInputStream, storepass.toCharArray());
PrivateKey key = (PrivateKey)keyStore.getKey(alias, keypass.toCharArray());
X509Certificate cert = (X509Certificate)keyStore.getCertificate(alias);
SigningKey sk = SigningKeyFactory.makeSigningKey(key);
KeyInfo ki = new KeyInfo();
ki.setCertificate(cert);
WSSecurity wSSecurity = new WSSecurity();
wSSecurity.sign(doc, sk, ki);//签名。
}
/**
*对XML文档进行身份验证。
*/
public static boolean verify(Document doc, String keystore, String storetype,
String storepass) throws Exception {
FileInputStream fileInputStream = new FileInputStream(keystore);
java.security.KeyStore keyStore = java.security.KeyStore.getInstance(storetype);
keyStore.load(fileInputStream, storepass.toCharArray());
TrustVerifier verifier = new X509TrustVerifier(keyStore);
WSSecurity wSSecurity = new WSSecurity();
MessageValidity[] resa = wSSecurity.verify(doc, verifier, null,null);
if (resa.length > 0)
return resa[0].isValid();
return false;
}
/**
*对XML文档进行加密。必须有JSSE提供者才能加密。
*/
public static void encrypt(Document doc, String keystore, String storetype,
String storepass, String alias) throws Exception {
try
{
FileInputStream fileInputStream = new FileInputStream(keystore);
java.security.KeyStore keyStore = java.security.KeyStore.getInstance(storetype);
keyStore.load(fileInputStream, storepass.toCharArray());
X509Certificate cert = (X509Certificate)keyStore.getCertificate(alias);
PublicKey pubk = cert.getPublicKey();
KeyGenerator keyGenerator = KeyGenerator.getInstance("DESede",PROVIDER);
keyGenerator.init(168, new SecureRandom());
SecretKey key = keyGenerator.generateKey();
KeyInfo ki = new KeyInfo();
ki.setCertificate(cert);
WSSecurity wSSecurity = new WSSecurity();
//加密。
wSSecurity.encrypt(doc, key, AlgorithmType.TRIPLEDES, pubk, AlgorithmType.RSA1_5, ki);
}
catch(Exception e)
{
e.printStackTrace();
}
}
/**
*对文档进行解密。
*/
public static void decrypt(Document doc, String keystore, String storetype,
String storepass, String alias, String keypass) throws Exception {
FileInputStream fileInputStream = new FileInputStream(keystore);
java.security.KeyStore keyStore = java.security.KeyStore.getInstance(storetype);
keyStore.load(fileInputStream, storepass.toCharArray());
PrivateKey prvk2 = (PrivateKey)keyStore.getKey(alias, keypass.toCharArray());
WSSecurity wSSecurity = new WSSecurity();
//解密。
wSSecurity.decrypt(doc, prvk2, null);
WsUtils.removeEncryptedKey(doc);//从 WS-Security Header中删除 EncryptedKey 元素
}
public static void removeWSSElements(Document doc) throws Exception {
WsUtils.removeWSSElements(doc);// 删除WSS相关的元素。
}
}
WSSHelper类中使用了ISNetworks安全提供者,ISNetworks实现了RSA加密、解密算法。当然,你也可以使用其它的安全提供者,并且可以使用不同的加密算法。可以从网络上下载ISNetworks相关包。
WSSHelper中包含了一个WsUtils类,它的功能就是从加密后的SOAP消息中删除一些WS-Security元素,删除这些元素后的SOAP消息才能被最终的客户端或者Web服务端处理。
服务器端Handler开发
当请求到达后,服务端Handler调用handleRequest方法,执行如下过程:对请求SOAP消息解密'身份验证'删除WSS元素'把Document转换成SOAP消息。 Web服务端点对请求做出响应后,将调用handleResponse方法,执行如下过程:对响应的SOAP消息进行数字签名'加密'把Document转换成SOAP消息。
例程8 服务器端Handler(WSSecurityServerHandler.java)
package com.hellking.study.webservice;
...
//服务器端Handler
public class WSSecurityServerHandler implements Handler
{
//密匙库相关信息
private String keyStoreFile = null;
private String keyStoreType = "JKS";
。。。
public WSSecurityServerHandler()
{
System.out.println("服务端Handler:构造方法");
}
/**
*处理请求
*流程:解密-->身份验证-->删除WSS元素'把Document转换成SOAP消息。
*/
public boolean handleRequest(MessageContext messageContext) {
System.out.println("开始处理请求。。。");
if (messageContext instanceof SOAPMessageContext){
try {
SOAPMessageContext soapMessageContext = (SOAPMessageContext)messageContext;
SOAPMessage soapMessage = soapMessageContext.getMessage();
soapMessage.writeTo(System.out);
Document doc = MessageConveter.convertSoapMessageToDocument(soapMessage);
//解密
WSSHelper.decrypt(doc, keyStoreFile, keyStoreType,
keyStorePassword, keyAlias, keyEntryPassword);
//身份验证
WSSHelper.verify(doc, trustStoreFile, trustStoreType, trustStorePassword);
//删除WSS元素
WSSHelper.removeWSSElements(doc);
soapMessage = MessageConveter.convertDocumentToSOAPMessage(doc);
soapMessageContext.setMessage(soapMessage);
} catch (Exception e){
System.err.println("在处理请求时发生了异常: " + e);
e.printStackTrace();
return false;
}
} else {
System.out.println("MessageContext是以下类的实例: " + messageContext.getClass());
}
System.out.println("处理请求完毕!");
return true;
}
/**
*处理响应
*流程:数字签名-->加密-->把Document转换成SOAP消息。
*/
public boolean handleResponse(MessageContext messageContext) {
System.out.println("开始处理Web服务响应。。。");
if (messageContext instanceof SOAPMessageContext){
try {
SOAPMessageContext soapMessageContext = (SOAPMessageContext)messageContext;
SOAPMessage soapMessage = soapMessageContext.getMessage();
Document doc = MessageConveter.convertSoapMessageToDocument(soapMessage);
WSSHelper.sign(doc, keyStoreFile, keyStoreType,
keyStorePassword, keyAlias, keyEntryPassword);
WSSHelper.encrypt(doc, trustStoreFile, trustStoreType,
trustStorePassword, certAlias);
soapMessage = MessageConveter.convertDocumentToSOAPMessage(doc);
soapMessageContext.setMessage(soapMessage);
} catch (Exception e){
System.err.println("在处理响应时发生以下错误: " + e);
e.printStackTrace();
return false;
}
}
System.out.println("处理响应完毕!");
return true;
}
/**
*初始化,主要是初始化一些相关参数。
*/
public void init(HandlerInfo config) {
System.out.println("WSSecurityServerHandler初始化");
Object param = "";
Map configs = config.getHandlerConfig();
keyStoreFile = (String)configs.get("keyStoreFile");
trustStoreFile = (String)configs.get("trustStoreFile");
…//其它参数初始化
}
…
}
客户端Handler开发
客户端Handler可以是任何JAX-RPC兼容的Handler处理器。比如AXIS Handler实现或者SUN 提供的JAX-RPC Handler参考实现。这里使用后者来作为客户端Handler处理器。
客户端Handler和服务器端Handler原理一样,但处理过程完全相反。
例程9 客户端Handler(WSSecurityClientHandler.java)
package com.hellking.study.webservice;
…
//客户端Handler
public class WSSecurityClientHandler implements Handler
{
//密匙库相关信息
...
/**
*处理请求
*流程:数字签名-->加密-->把Document转换成SOAP消息。
*/
public boolean handleRequest(MessageContext messageContext) {
System.out.println("开始处理请求。。。");
…
WSSHelper.sign(doc, keyStoreFile, keyStoreType,
keyStorePassword, keyAlias, keyEntryPassword);
WSSHelper.encrypt(doc, trustStoreFile, trustStoreType,
trustStorePassword, certAlias);
soapMessage = MessageConveter.convertDocumentToSOAPMessage(doc);
soapMessageContext.setMessage(soapMessage);
…
System.out.println("处理请求完毕!");
return true;
}
/**
*处理响应
*流程:解密-->身份验证-->删除WSS元素'把Document转换成SOAP消息。
*/
public boolean handleResponse(MessageContext messageContext) {
System.out.println("开始处理Web服务响应。。。");
…
WSSHelper.decrypt(doc, keyStoreFile, keyStoreType,
keyStorePassword, keyAlias, keyEntryPassword);
WSSHelper.verify(doc, trustStoreFile, trustStoreType, trustStorePassword);
WSSHelper.removeWSSElements(doc);
soapMessage = MessageConveter.convertDocumentToSOAPMessage(doc);
System.out.println("the final message is:");
soapMessage.writeTo(System.out);
soapMessageContext.setMessage(soapMessage);
…
System.out.println("处理响应完毕!");
return true;
}
/**
*初始化,主要是初始化一些相关参数。
*/
public void init(HandlerInfo config) {
…
}
…
}
部署服务器端Handler
为了使用Handler,需要在Web服务部署描述符中指定使用此Handler。Handler包含的初始化参数也在此描述,如例程10所示。
例程10 服务器端Handler部署代码
<service name="PersonalTaxServicePort" provider="java:RPC">
<parameter name="allowedMethods" value="*"/>
<parameter name="className" value="com.hellking.study.webservice.PersonalTaxService"/>
<parameter name="wsdlTargetNamespace" value="http://hellking.webservices.com/"/>
<parameter name="wsdlServiceElement" value="PersonalTaxService"/>
<parameter name="wsdlServicePort" value="PersonalTaxServicePort"/>
<parameter name="wsdlPortType" value="PersonalTaxService"/>
<requestFlow>
<handler type="java:org.apache.axis.handlers.JAXRPCHandler">
<parameter name="scope" value="session"/>
<parameter name="className"
value="com.hellking.study.webservice.WSSecurityServerHandler"/>
<parameter name="keyStoreFile"
value="K:\\jakarta-tomcat-5.0.16\\server.keystore"/>
<parameter name="trustStoreFile"
value="K:\\jakarta-tomcat-5.0.16\\server.truststore"/>
<parameter name="certAlias" value="clientkey"/>
</handler>
</requestFlow>
<responseFlow>
<handler type="java:org.apache.axis.handlers.JAXRPCHandler">
<parameter name="scope" value="session"/>
<parameter name="className"
value="com.hellking.study.webservice.WSSecurityServerHandler"/>
<parameter name="keyStoreFile"
value="K:\\jakarta-tomcat-5.0.16\\server.keystore"/>
<parameter name="trustStoreFile"
value="K:\\jakarta-tomcat-5.0.16\\server.truststore"/>
<parameter name="certAlias" value="clientkey"/>
</handler>
</responseFlow>
</service>
requestFlow表示Web服务PersonalTaxServicePort的请求处理Handler链。这里只有一个Handler,就是WSSecurityServerHandler。当Web服务请求到达PersonalTaxServicePort时,WSSecurityServerHandler的handleRequest方法将被自动调用。
注意:部署时,请改变Handler相关参数以和目标的Web服务一致,比如trustStoreFile的路径等。
调用测试
这里采用代理的方式来调用Web服务,先编写一个Web服务接口。
例程11 TaxServiceInterface
package com.hellking.study.webservice;
…
/**
*个人所得税Web服务。
*/
public interface TaxServiceInterface extends Remote
{
public double getTax(double salary)throws java.rmi.RemoteException;
}
WSSClient客户端程序是通过代理的方式来访问Web服务的。由于要使用Handler,所以在访问前通过registerHandlers()方法注册了WSSecurityClientHandler,并且初始化了WSSecurityClientHandler的相关参数。当然,JAX-RPC"参考实现"还支持在Web服务客户端配置文件中描述Handler信息,这样就不需要在客户端代码中对Handler进行注册了,你可以参考相关文档。
例程12 测试客户端程序(WSSClient)
package com.hellking.study.webservice;
...
/**
*调用需要验证的Web服务
*/
public class WSSClient
{
static final double salary=5000;
public static void main(String [] args)
{
try {
//服务端的url,需要根据情况更改。
String endpointURL = "http://localhost:8080/axis/services/PersonalTaxServicePort";
String wsdlURL=endpointURL+"?wsdl";
java.net.URL targetURL= new java.net.URL(wsdlURL);
String nameSpaceUri = "http://hellking.webservices.com/";
String svcName = "PersonalTaxService";
String portName = "PersonalTaxServicePort";
ServiceFactory svcFactory = ServiceFactory.newInstance();
Service svc = svcFactory.createService(targetURL, new QName(nameSpaceUri, svcName));
//cfg表示客户端的配置信息。
java.util.HashMap cfg = new java.util.HashMap();
cfg.put("keyStoreFile", "client.keystore");
cfg.put("trustStoreFile", "client.truststore");
cfg.put("certAlias", "changeit");
Class hdlrClass = com.hellking.study.webservice.WSSecurityClientHandler.class;
java.util.List list = svc.getHandlerRegistry().
getHandlerChain(new QName(nameSpaceUri, portName));
list.add(new javax.xml.rpc.handler.HandlerInfo(hdlrClass, cfg, null));
registerHandlers (svc);
TaxServiceInterface myProxy =
( TaxServiceInterface) svc.getPort(new QName(nameSpaceUri, portName),
TaxServiceInterface.class);
double ret=myProxy.getTax(5000);
System.out.println("使用HTTP协议来作为Web服务的传输协议!");
System.out.println("已经成功调用。请参看服务端的输出!");
System.out.println("输入工资"+salary+"元,应交个人所得税:"+ret);
} catch (Exception e) {
e.printStackTrace();
}
}
//注册Handler
private static void registerHandlers ( Service service )
throws javax.xml.rpc.ServiceException {
java.util.HashMap cfg = new java.util.HashMap();
cfg.put("keyStoreFile", "client.keystore");
cfg.put("trustStoreFile", "client.truststore");
cfg.put("certAlias", "changeit");
/*
* 封装客户端Handler到HandlerInfo 中,然后添加到Handler链中。
*/
javax.xml.rpc.handler.HandlerInfo info = new javax.xml.rpc.handler.HandlerInfo
(com.hellking.study.webservice.WSSecurityClientHandler.class, cfg, null );
java.util.ArrayList handlerList = new java.util.ArrayList();
handlerList.add(info);
/*
* 获得Handler注册
*/
javax.xml.rpc.handler.HandlerRegistry handlerRegistry = service.getHandlerRegistry();
/*
* 把Handler添加到所有的port中。
*/
java.util.Iterator portIterator = service.getPorts();
while ( portIterator.hasNext()) {
Object obj=portIterator.next();
QName portName = (QName) obj;
handlerRegistry.setHandlerChain(portName, handlerList);
}
}
}
注意:由于客户端使用了SUN公司提供的"JAX-RPC参考实现",所以必须把jaxrpc-impl.jar包设置在CLASSPATH环境变量中,并且不要把axis.jar设置在客户端CLASSPATH环境变量,否则会出现ClassCastException异常。这是因为axis也是JAX-RPC的实现,如果它在CLASSPATH环境变量中,当调用:
ServiceFactory svcFactory = ServiceFactory.newInstance()方法时,就可能初始化一个axis的ServiceFactory 实现。
本文源代码中client目录下wss-client.bat文件包含了执行WSSClient脚本,修改了部分环境变量参数后,才能执行。
Trackback: http://tb.blog.youkuaiyun.com/TrackBack.aspx?PostId=667538
2007-01-17 08:54 P.M.
签名、加密、解密、身份验证的实现
对SOAP消息的签名、加密、解密、身份验证都放在一个名为WSSHelper的类中进行。
例程7 签名、加密、解密、身份验证功能的实现――WSSHelper.java
package com.hellking.study.webservice;
import com.verisign.messaging.WSSecurity;
...
public class WSSHelper {
static String PROVIDER="ISNetworks";//JSSE安全提供者。
//添加JSSE安全提供者,你也可以使用其它安全提供者。只要支持DESede算法。
static
{
java.security.Security.addProvider(new com.isnetworks.provider.jce.ISNetworksProvider());
}
/**
*对XML文档进行数字签名。
*/
public static void sign(Document doc, String keystore, String storetype,
String storepass, String alias, String keypass) throws Exception {
FileInputStream fileInputStream = new FileInputStream(keystore);
java.security.KeyStore keyStore = java.security.KeyStore.getInstance(storetype);
keyStore.load(fileInputStream, storepass.toCharArray());
PrivateKey key = (PrivateKey)keyStore.getKey(alias, keypass.toCharArray());
X509Certificate cert = (X509Certificate)keyStore.getCertificate(alias);
SigningKey sk = SigningKeyFactory.makeSigningKey(key);
KeyInfo ki = new KeyInfo();
ki.setCertificate(cert);
WSSecurity wSSecurity = new WSSecurity();
wSSecurity.sign(doc, sk, ki);//签名。
}
/**
*对XML文档进行身份验证。
*/
public static boolean verify(Document doc, String keystore, String storetype,
String storepass) throws Exception {
FileInputStream fileInputStream = new FileInputStream(keystore);
java.security.KeyStore keyStore = java.security.KeyStore.getInstance(storetype);
keyStore.load(fileInputStream, storepass.toCharArray());
TrustVerifier verifier = new X509TrustVerifier(keyStore);
WSSecurity wSSecurity = new WSSecurity();
MessageValidity[] resa = wSSecurity.verify(doc, verifier, null,null);
if (resa.length > 0)
return resa[0].isValid();
return false;
}
/**
*对XML文档进行加密。必须有JSSE提供者才能加密。
*/
public static void encrypt(Document doc, String keystore, String storetype,
String storepass, String alias) throws Exception {
try
{
FileInputStream fileInputStream = new FileInputStream(keystore);
java.security.KeyStore keyStore = java.security.KeyStore.getInstance(storetype);
keyStore.load(fileInputStream, storepass.toCharArray());
X509Certificate cert = (X509Certificate)keyStore.getCertificate(alias);
PublicKey pubk = cert.getPublicKey();
KeyGenerator keyGenerator = KeyGenerator.getInstance("DESede",PROVIDER);
keyGenerator.init(168, new SecureRandom());
SecretKey key = keyGenerator.generateKey();
KeyInfo ki = new KeyInfo();
ki.setCertificate(cert);
WSSecurity wSSecurity = new WSSecurity();
//加密。
wSSecurity.encrypt(doc, key, AlgorithmType.TRIPLEDES, pubk, AlgorithmType.RSA1_5, ki);
}
catch(Exception e)
{
e.printStackTrace();
}
}
/**
*对文档进行解密。
*/
public static void decrypt(Document doc, String keystore, String storetype,
String storepass, String alias, String keypass) throws Exception {
FileInputStream fileInputStream = new FileInputStream(keystore);
java.security.KeyStore keyStore = java.security.KeyStore.getInstance(storetype);
keyStore.load(fileInputStream, storepass.toCharArray());
PrivateKey prvk2 = (PrivateKey)keyStore.getKey(alias, keypass.toCharArray());
WSSecurity wSSecurity = new WSSecurity();
//解密。
wSSecurity.decrypt(doc, prvk2, null);
WsUtils.removeEncryptedKey(doc);//从 WS-Security Header中删除 EncryptedKey 元素
}
public static void removeWSSElements(Document doc) throws Exception {
WsUtils.removeWSSElements(doc);// 删除WSS相关的元素。
}
}
WSSHelper类中使用了ISNetworks安全提供者,ISNetworks实现了RSA加密、解密算法。当然,你也可以使用其它的安全提供者,并且可以使用不同的加密算法。可以从网络上下载ISNetworks相关包。
WSSHelper中包含了一个WsUtils类,它的功能就是从加密后的SOAP消息中删除一些WS-Security元素,删除这些元素后的SOAP消息才能被最终的客户端或者Web服务端处理。
服务器端Handler开发
当请求到达后,服务端Handler调用handleRequest方法,执行如下过程:对请求SOAP消息解密'身份验证'删除WSS元素'把Document转换成SOAP消息。 Web服务端点对请求做出响应后,将调用handleResponse方法,执行如下过程:对响应的SOAP消息进行数字签名'加密'把Document转换成SOAP消息。
例程8 服务器端Handler(WSSecurityServerHandler.java)
package com.hellking.study.webservice;
...
//服务器端Handler
public class WSSecurityServerHandler implements Handler
{
//密匙库相关信息
private String keyStoreFile = null;
private String keyStoreType = "JKS";
。。。
public WSSecurityServerHandler()
{
System.out.println("服务端Handler:构造方法");
}
/**
*处理请求
*流程:解密-->身份验证-->删除WSS元素'把Document转换成SOAP消息。
*/
public boolean handleRequest(MessageContext messageContext) {
System.out.println("开始处理请求。。。");
if (messageContext instanceof SOAPMessageContext){
try {
SOAPMessageContext soapMessageContext = (SOAPMessageContext)messageContext;
SOAPMessage soapMessage = soapMessageContext.getMessage();
soapMessage.writeTo(System.out);
Document doc = MessageConveter.convertSoapMessageToDocument(soapMessage);
//解密
WSSHelper.decrypt(doc, keyStoreFile, keyStoreType,
keyStorePassword, keyAlias, keyEntryPassword);
//身份验证
WSSHelper.verify(doc, trustStoreFile, trustStoreType, trustStorePassword);
//删除WSS元素
WSSHelper.removeWSSElements(doc);
soapMessage = MessageConveter.convertDocumentToSOAPMessage(doc);
soapMessageContext.setMessage(soapMessage);
} catch (Exception e){
System.err.println("在处理请求时发生了异常: " + e);
e.printStackTrace();
return false;
}
} else {
System.out.println("MessageContext是以下类的实例: " + messageContext.getClass());
}
System.out.println("处理请求完毕!");
return true;
}
/**
*处理响应
*流程:数字签名-->加密-->把Document转换成SOAP消息。
*/
public boolean handleResponse(MessageContext messageContext) {
System.out.println("开始处理Web服务响应。。。");
if (messageContext instanceof SOAPMessageContext){
try {
SOAPMessageContext soapMessageContext = (SOAPMessageContext)messageContext;
SOAPMessage soapMessage = soapMessageContext.getMessage();
Document doc = MessageConveter.convertSoapMessageToDocument(soapMessage);
WSSHelper.sign(doc, keyStoreFile, keyStoreType,
keyStorePassword, keyAlias, keyEntryPassword);
WSSHelper.encrypt(doc, trustStoreFile, trustStoreType,
trustStorePassword, certAlias);
soapMessage = MessageConveter.convertDocumentToSOAPMessage(doc);
soapMessageContext.setMessage(soapMessage);
} catch (Exception e){
System.err.println("在处理响应时发生以下错误: " + e);
e.printStackTrace();
return false;
}
}
System.out.println("处理响应完毕!");
return true;
}
/**
*初始化,主要是初始化一些相关参数。
*/
public void init(HandlerInfo config) {
System.out.println("WSSecurityServerHandler初始化");
Object param = "";
Map configs = config.getHandlerConfig();
keyStoreFile = (String)configs.get("keyStoreFile");
trustStoreFile = (String)configs.get("trustStoreFile");
…//其它参数初始化
}
…
}
客户端Handler开发
客户端Handler可以是任何JAX-RPC兼容的Handler处理器。比如AXIS Handler实现或者SUN 提供的JAX-RPC Handler参考实现。这里使用后者来作为客户端Handler处理器。
客户端Handler和服务器端Handler原理一样,但处理过程完全相反。
例程9 客户端Handler(WSSecurityClientHandler.java)
package com.hellking.study.webservice;
…
//客户端Handler
public class WSSecurityClientHandler implements Handler
{
//密匙库相关信息
...
/**
*处理请求
*流程:数字签名-->加密-->把Document转换成SOAP消息。
*/
public boolean handleRequest(MessageContext messageContext) {
System.out.println("开始处理请求。。。");
…
WSSHelper.sign(doc, keyStoreFile, keyStoreType,
keyStorePassword, keyAlias, keyEntryPassword);
WSSHelper.encrypt(doc, trustStoreFile, trustStoreType,
trustStorePassword, certAlias);
soapMessage = MessageConveter.convertDocumentToSOAPMessage(doc);
soapMessageContext.setMessage(soapMessage);
…
System.out.println("处理请求完毕!");
return true;
}
/**
*处理响应
*流程:解密-->身份验证-->删除WSS元素'把Document转换成SOAP消息。
*/
public boolean handleResponse(MessageContext messageContext) {
System.out.println("开始处理Web服务响应。。。");
…
WSSHelper.decrypt(doc, keyStoreFile, keyStoreType,
keyStorePassword, keyAlias, keyEntryPassword);
WSSHelper.verify(doc, trustStoreFile, trustStoreType, trustStorePassword);
WSSHelper.removeWSSElements(doc);
soapMessage = MessageConveter.convertDocumentToSOAPMessage(doc);
System.out.println("the final message is:");
soapMessage.writeTo(System.out);
soapMessageContext.setMessage(soapMessage);
…
System.out.println("处理响应完毕!");
return true;
}
/**
*初始化,主要是初始化一些相关参数。
*/
public void init(HandlerInfo config) {
…
}
…
}
部署服务器端Handler
为了使用Handler,需要在Web服务部署描述符中指定使用此Handler。Handler包含的初始化参数也在此描述,如例程10所示。
例程10 服务器端Handler部署代码
<service name="PersonalTaxServicePort" provider="java:RPC">
<parameter name="allowedMethods" value="*"/>
<parameter name="className" value="com.hellking.study.webservice.PersonalTaxService"/>
<parameter name="wsdlTargetNamespace" value="http://hellking.webservices.com/"/>
<parameter name="wsdlServiceElement" value="PersonalTaxService"/>
<parameter name="wsdlServicePort" value="PersonalTaxServicePort"/>
<parameter name="wsdlPortType" value="PersonalTaxService"/>
<requestFlow>
<handler type="java:org.apache.axis.handlers.JAXRPCHandler">
<parameter name="scope" value="session"/>
<parameter name="className"
value="com.hellking.study.webservice.WSSecurityServerHandler"/>
<parameter name="keyStoreFile"
value="K:\\jakarta-tomcat-5.0.16\\server.keystore"/>
<parameter name="trustStoreFile"
value="K:\\jakarta-tomcat-5.0.16\\server.truststore"/>
<parameter name="certAlias" value="clientkey"/>
</handler>
</requestFlow>
<responseFlow>
<handler type="java:org.apache.axis.handlers.JAXRPCHandler">
<parameter name="scope" value="session"/>
<parameter name="className"
value="com.hellking.study.webservice.WSSecurityServerHandler"/>
<parameter name="keyStoreFile"
value="K:\\jakarta-tomcat-5.0.16\\server.keystore"/>
<parameter name="trustStoreFile"
value="K:\\jakarta-tomcat-5.0.16\\server.truststore"/>
<parameter name="certAlias" value="clientkey"/>
</handler>
</responseFlow>
</service>
requestFlow表示Web服务PersonalTaxServicePort的请求处理Handler链。这里只有一个Handler,就是WSSecurityServerHandler。当Web服务请求到达PersonalTaxServicePort时,WSSecurityServerHandler的handleRequest方法将被自动调用。
注意:部署时,请改变Handler相关参数以和目标的Web服务一致,比如trustStoreFile的路径等。
调用测试
这里采用代理的方式来调用Web服务,先编写一个Web服务接口。
例程11 TaxServiceInterface
package com.hellking.study.webservice;
…
/**
*个人所得税Web服务。
*/
public interface TaxServiceInterface extends Remote
{
public double getTax(double salary)throws java.rmi.RemoteException;
}
WSSClient客户端程序是通过代理的方式来访问Web服务的。由于要使用Handler,所以在访问前通过registerHandlers()方法注册了WSSecurityClientHandler,并且初始化了WSSecurityClientHandler的相关参数。当然,JAX-RPC"参考实现"还支持在Web服务客户端配置文件中描述Handler信息,这样就不需要在客户端代码中对Handler进行注册了,你可以参考相关文档。
例程12 测试客户端程序(WSSClient)
package com.hellking.study.webservice;
...
/**
*调用需要验证的Web服务
*/
public class WSSClient
{
static final double salary=5000;
public static void main(String [] args)
{
try {
//服务端的url,需要根据情况更改。
String endpointURL = "http://localhost:8080/axis/services/PersonalTaxServicePort";
String wsdlURL=endpointURL+"?wsdl";
java.net.URL targetURL= new java.net.URL(wsdlURL);
String nameSpaceUri = "http://hellking.webservices.com/";
String svcName = "PersonalTaxService";
String portName = "PersonalTaxServicePort";
ServiceFactory svcFactory = ServiceFactory.newInstance();
Service svc = svcFactory.createService(targetURL, new QName(nameSpaceUri, svcName));
//cfg表示客户端的配置信息。
java.util.HashMap cfg = new java.util.HashMap();
cfg.put("keyStoreFile", "client.keystore");
cfg.put("trustStoreFile", "client.truststore");
cfg.put("certAlias", "changeit");
Class hdlrClass = com.hellking.study.webservice.WSSecurityClientHandler.class;
java.util.List list = svc.getHandlerRegistry().
getHandlerChain(new QName(nameSpaceUri, portName));
list.add(new javax.xml.rpc.handler.HandlerInfo(hdlrClass, cfg, null));
registerHandlers (svc);
TaxServiceInterface myProxy =
( TaxServiceInterface) svc.getPort(new QName(nameSpaceUri, portName),
TaxServiceInterface.class);
double ret=myProxy.getTax(5000);
System.out.println("使用HTTP协议来作为Web服务的传输协议!");
System.out.println("已经成功调用。请参看服务端的输出!");
System.out.println("输入工资"+salary+"元,应交个人所得税:"+ret);
} catch (Exception e) {
e.printStackTrace();
}
}
//注册Handler
private static void registerHandlers ( Service service )
throws javax.xml.rpc.ServiceException {
java.util.HashMap cfg = new java.util.HashMap();
cfg.put("keyStoreFile", "client.keystore");
cfg.put("trustStoreFile", "client.truststore");
cfg.put("certAlias", "changeit");
/*
* 封装客户端Handler到HandlerInfo 中,然后添加到Handler链中。
*/
javax.xml.rpc.handler.HandlerInfo info = new javax.xml.rpc.handler.HandlerInfo
(com.hellking.study.webservice.WSSecurityClientHandler.class, cfg, null );
java.util.ArrayList handlerList = new java.util.ArrayList();
handlerList.add(info);
/*
* 获得Handler注册
*/
javax.xml.rpc.handler.HandlerRegistry handlerRegistry = service.getHandlerRegistry();
/*
* 把Handler添加到所有的port中。
*/
java.util.Iterator portIterator = service.getPorts();
while ( portIterator.hasNext()) {
Object obj=portIterator.next();
QName portName = (QName) obj;
handlerRegistry.setHandlerChain(portName, handlerList);
}
}
}
注意:由于客户端使用了SUN公司提供的"JAX-RPC参考实现",所以必须把jaxrpc-impl.jar包设置在CLASSPATH环境变量中,并且不要把axis.jar设置在客户端CLASSPATH环境变量,否则会出现ClassCastException异常。这是因为axis也是JAX-RPC的实现,如果它在CLASSPATH环境变量中,当调用:
ServiceFactory svcFactory = ServiceFactory.newInstance()方法时,就可能初始化一个axis的ServiceFactory 实现。
本文源代码中client目录下wss-client.bat文件包含了执行WSSClient脚本,修改了部分环境变量参数后,才能执行。
Trackback: http://tb.blog.youkuaiyun.com/TrackBack.aspx?PostId=667538