精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2010-03-04
最后修改:2011-06-23
Java 调用.net发布的Web service可以通过众多的java lib来实现 本文选择Axis-1.4来说明如何调用.net webservice
(一)通过WSDL文件生成Java代码 1.先得到.net webservice的WSDL文件,可以这样得到:https://xxx.com.hk/bcmedservice/BCWebServices.asmx?WSDL或者https://xxx.com.hk/bcmedservice/BCWebServices.asmx?WSDL 2.通过org.apache.axis.wsdl.WSDL2Java类结合得到的WSDL文件生成Java代码。因生成的文件较多,不便分析,我将它改成了三个文件,若.net那边没有封装参数和结果,只需要一个文件即可。文件代码如下: 1)
import java.security.Security; import org.apache.axis.client.Call; import org.apache.axis.client.Service; public class BCWebService { public static String checkMember(java.lang.String medicalGroup, java.lang.String requestTicket, java.lang.String userID, java.lang.String password, java.lang.String claimDate, java.lang.String memberKey1, java.lang.String memberKey2, java.lang.String doctorCode) { String endpoint = "https://xxx.com.hk/bcmedservice/BCWebServices.asmx"; try {
//以下是调用https需要用到的安全证书,下面会介绍如何下载安全证书 System.setProperty("javax.net.ssl.keyStore", "D:/xxx/jssecacerts");
Service service = new Service(); Call call = (Call) service.createCall(); call.setTargetEndpointAddress(new java.net.URL(endpoint)); call.setUseSOAPAction(true); call.setSOAPActionURI("https://xxx.com.hk/bcmedservice/Check_Member_Eligibility"); call.setEncodingStyle(null); call.setProperty(org.apache.axis.client.Call.SEND_TYPE_ATTR, Boolean.FALSE); call.setProperty(org.apache.axis.AxisEngine.PROP_DOMULTIREFS, Boolean.FALSE); call.setOperationName(new javax.xml.namespace.QName( "https://xxx.com.hk/bcmedservice/", "Check_Member_Eligibility"));
call.addParameter(new javax.xml.namespace.QName( "https://xxx.com.hk/bcmedservice/", "objParameter"), new javax.xml.namespace.QName( "https://xxx.com.hk/bcmedservice/", "Check_Member_Eligibility"), InputParameter.class, javax.xml.rpc.ParameterMode.IN); call.setReturnType(new javax.xml.namespace.QName( "https://xxx.com.hk/bcmedservice/", "Check_Member_EligibilityResponse")); call.setReturnClass(OutputResponse.class); OutputResponse or2 = (OutputResponse)call.invoke(new java.lang.Object[] { new InputParameter(medicalGroup, requestTicket, userID, password,claimDate, memberKey1, memberKey2, doctorCode) });
if (or2 != null) {
StringBuffer sb = new StringBuffer(); sb.append("<OutputResponse><RequestTicket>"); sb.append(or2.getRequestTicket()); sb.append("</RequestTicket><ResponseTicket>"); sb.append(or2.getResponseTicket()); sb.append("</ResponseTicket><ResponseCode>"); sb.append(or2.getResponseCode()); sb.append("</ResponseCode><ResponseMessageEng>"); sb.append(or2.getResponseMessageEng()); sb.append("</ResponseMessageEng><ResponseMessageChi>"); sb.append(or2.getResponseMessageChi()); sb.append("</ResponseMessageChi><TechnicalRemarks>"); sb.append(or2.getTechnicalRemarks()); sb.append("</TechnicalRemarks><InsuredName>"); sb.append(or2.getInsuredName()); sb.append("</InsuredName><GPCopayAmt>"); sb.append(or2.getGPCopayAmt()); sb.append("</GPCopayAmt><GPXMedAmt>"); sb.append(or2.getGPXMedAmt()); sb.append("</GPXMedAmt><SPCopayAmt>"); sb.append(or2.getSPCopayAmt()); sb.append("</SPCopayAmt><SPXMedAmt>"); sb.append(or2.getSPXMedAmt()); sb.append("</SPXMedAmt></OutputResponse>"); return sb.toString(); } else { return null; } } catch (Exception e) { e.printStackTrace(); return null; } }
}
其中的InputParameterl类和OutputResponse类是由WSDL2Java生成的 (二)调用非 SSL 发布的 webservice 若调用非 SSL 发布的 webservice,只需要将 System.setProperty("javax.net.ssl.trustStore", "D:/xxx/jssecacerts");去掉即可 (三)调用SSL发布的webservice 这种情况需要下载安全证书,可以通过运行一个java类来实现,具体参考我的另外一篇博客: PKIX path building failed 的问题,请注意证书的应用要重新启动应用服务器(如tomcat server)。因前面的方法是静态的方法,不是动态过程中能加载这个证书的。 证书得到后,按照上面的BCWebservice.java就可以调用.net webServcie了。有一个问题需要提到的是,如果安全证书经常更新变化,又不想每次也跟着重新下载证书,希望程序自动取验证。我当初想到,这还不容易,通过程序运行刚才下载证书的代码不就可以重新生成证书了?问题是,就算是能下载到新的证书,这个新的也用不上。除非你重启web服务器,或重新加载调用webservice的项目,若是Application,也需要关闭,重新开启程序才可以。因为这个证书是在程序第一次运行时加载的,而且axis会将这个东西缓存起来,缓存起来的东西叫SSLSocketFactory 的一个实例。而这个SSLSocketFactory 的初始化是Axis自动执行的。 我现在希望是应用程序不需重新启动,希望证书的下载和验证都通过程序来执行,怎么做?就是在运行时能重新加载和验证新的证书SocketFactory被实例化之前,应该已经验证了证书,否则它无法访问SSL的webservice。既然这样,能否自己初始化SocketFacotry呢,那是肯定的。可以写一个类重新实现SocketFactory,但要遵循一些规则。一下是一个示例:
public class MyCustomSSLSocketFactory extends JSSESocketFactory implements SecureSocketFactory {
/* local keystore password */ private static String MY_KEYSTORE_PASSWORD = "changeit";
/* local keystore file (contains the self-signed certificate from the server */ private static String RESOURCE_PATH_TO_KEYSTORE = "D:/UTA/DOC_E_Health_XML/Keystore/bluecrossCertificate";
/** * Constructor MyCustomSSLSocketFactory * * @param attributes */ public MyCustomSSLSocketFactory(Hashtable attributes) { super(attributes);
}
/** * Read the keystore, init the SSL socket factory * * This overrides the parent class to provide our SocketFactory implementation. * @throws IOException */ protected void initFactory() throws IOException {
try { SSLContext context = getContext(); sslFactory = context.getSocketFactory(); } catch (Exception e) { e.printStackTrace(); if (e instanceof IOException) { throw (IOException) e; } throw new IOException(e.getMessage());
} }
/** * Gets a custom SSL Context. * This is the main working of this class. The following are the steps that make up our * custom configuration: * * 1. Open our keystore file using the password provided * 2. Create a KeyManagerFactory and TrustManagerFactory using this file * 3. Initialise a SSLContext using these factories * * @return SSLContext * @throws WebServiceClientConfigException * @throws Exception */ protected SSLContext getContext() throws Exception {
String host="xxx.com.hk"; //这是你要访问的WEbservice的域名
try { // create required keystores and their corresponding manager objects /*KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(keystoreFile, keystorepass);
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); kmf.init(keyStore, keystorepass);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init(keyStore);
// congifure a local SSLContext to use created keystores SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom()); */
char[] passphrase=MY_KEYSTORE_PASSWORD.toCharArray();
File file = new File(RESOURCE_PATH_TO_KEYSTORE); if (file.isFile() == false) { char SEP = File.separatorChar; File dir = new File(System.getProperty("java.home") +SEP + "lib" + SEP + "security");
file = new File(dir, "jssecacerts"); if (file.isFile() == false) { file = new File(dir, "cacerts"); } }
InputStream in = new FileInputStream(file); KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); ks.load(in, passphrase); in.close(); SSLContext context = SSLContext.getInstance("TLS"); KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); kmf.init(ks, passphrase); TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init(ks); X509TrustManager defaultTrustManager = (X509TrustManager)tmf.getTrustManagers()[0]; SavingTrustManager tm = new SavingTrustManager(defaultTrustManager); context.init(null, new TrustManager[] {tm}, null); SSLSocketFactory factory = context.getSocketFactory();
SSLSocket socket = (SSLSocket)factory.createSocket(host, 443); socket.setSoTimeout(10000); try {
socket.startHandshake(); socket.close(); System.out.println(); System.out.println("No errors, certificate is already trusted"); } catch (SSLException e) { System.out.println(); System.out.println("***************Succeed to regenerate certificate***************"); //e.printStackTrace(System.out); }
X509Certificate[] chain = tm.chain; if (chain == null) { System.out.println("Could not obtain server certificate chain");
}
MessageDigest sha1 = MessageDigest.getInstance("SHA1"); MessageDigest md5 = MessageDigest.getInstance("MD5"); for (int i = 0; i < chain.length; i++) { X509Certificate cert = chain[i];
sha1.update(cert.getEncoded());
md5.update(cert.getEncoded());
System.out.println(); }
int k=0;
X509Certificate cert = chain[k]; String alias = "xxx.com.hk" + "-" + (k + 1); ks.setCertificateEntry(alias, cert); OutputStream out = new FileOutputStream(RESOURCE_PATH_TO_KEYSTORE); ks.store(out, passphrase); out.close(); in = new FileInputStream(new File(RESOURCE_PATH_TO_KEYSTORE)); ks.load(in,passphrase); in.close(); kmf.init(ks, passphrase); tmf.init(ks); context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom());
return context; } catch (Exception e) { throw new Exception("Error creating context for SSLSocket!", e); } }
private static class SavingTrustManager implements X509TrustManager { private final X509TrustManager tm; private X509Certificate[] chain; SavingTrustManager(X509TrustManager tm) { this.tm = tm; } public X509Certificate[] getAcceptedIssuers() { throw new UnsupportedOperationException(); } public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { throw new UnsupportedOperationException(); } public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { this.chain = chain; tm.checkServerTrusted(chain, authType); } } }
生成后我怎么让AXis用它呢,之需在BCWebservice.java中增加如下语句: AxisProperties.setProperty("axis.socketSecureFactory","com.xxx.webservice.MyCustomSSLSocketFactory");
问题到这儿还没结束,虽然我重写了SocketFactory,但是这个SocketFactory仍然被SocketFactoryFactory单例化了,所以还得改写SocketFactoryFactory,但这个时候不可以自定义,必须完全替换掉axis库中的 这个类,因为axis没法在运行时变换这个SocketFactoryFactory类的实例。替换的类如下: 红色部分为修改的代码
public class SocketFactoryFactory { /** Field log */ protected static Log log = LogFactory.getLog(SocketFactoryFactory.class.getName()); /** socket factory */ private static Hashtable factories = new Hashtable(); private static final Class classes[] = new Class[] { Hashtable.class }; static { AxisProperties.setClassOverrideProperty(SocketFactory.class, "axis.socketFactory"); AxisProperties.setClassDefault(SocketFactory.class, "org.apache.axis.components.net.DefaultSocketFactory"); AxisProperties.setClassOverrideProperty(SecureSocketFactory.class, "axis.socketSecureFactory"); AxisProperties.setClassDefault(SecureSocketFactory.class, "org.apache.axis.components.net.JSSESocketFactory"); }
/** * Returns a copy of the environment's default socket factory. * * @param protocol Today this only supports "http" & "https". * @param attributes * * @return */ public static synchronized SocketFactory getFactory(String protocol, Hashtable attributes) { SocketFactory theFactory = (SocketFactory)factories.get(protocol);
Object objects[] = new Object[] { attributes };
if (protocol.equalsIgnoreCase("http")) { if(theFactory==null) theFactory = (SocketFactory) AxisProperties.newInstance(SocketFactory.class, classes, objects); } else if (protocol.equalsIgnoreCase("https")) { System.out.println(); System.out.println("*********get a new SocketFacotory instance***********"); return (SecureSocketFactory) AxisProperties.newInstance(SecureSocketFactory.class, classes, objects);
}
if (theFactory != null) { factories.put(protocol, theFactory); }
return theFactory; } } 最终的BCWebserivce.java如下:
import java.security.Security; import org.apache.axis.client.Call; import org.apache.axis.client.Service; public class BCWebService { public static String checkMember(java.lang.String medicalGroup, java.lang.String requestTicket, java.lang.String userID, java.lang.String password, java.lang.String claimDate, java.lang.String memberKey1, java.lang.String memberKey2, java.lang.String doctorCode) { String endpoint = "https://xxx.com.hk/bcmedservice/BCWebServices.asmx"; try {
AxisProperties.setProperty("axis.socketSecureFactory","com.xxx.webservice.MyCustomSSLSocketFactory"); Service service = new Service(); Call call = (Call) service.createCall(); call.setTargetEndpointAddress(new java.net.URL(endpoint)); call.setUseSOAPAction(true); call.setSOAPActionURI("https://xxx.com.hk/bcmedservice/Check_Member_Eligibility"); call.setEncodingStyle(null); call.setProperty(org.apache.axis.client.Call.SEND_TYPE_ATTR, Boolean.FALSE); call.setProperty(org.apache.axis.AxisEngine.PROP_DOMULTIREFS, Boolean.FALSE); call.setOperationName(new javax.xml.namespace.QName( "https://xxx.com.hk/bcmedservice/", "Check_Member_Eligibility"));
call.addParameter(new javax.xml.namespace.QName( "https://xxx.com.hk/bcmedservice/", "objParameter"), new javax.xml.namespace.QName( "https://xxx.com.hk/bcmedservice/", "Check_Member_Eligibility"), InputParameter.class, javax.xml.rpc.ParameterMode.IN); call.setReturnType(new javax.xml.namespace.QName( "https://xxx.com.hk/bcmedservice/", "Check_Member_EligibilityResponse")); call.setReturnClass(OutputResponse.class); OutputResponse or2 = (OutputResponse)call.invoke(new java.lang.Object[] { new InputParameter(medicalGroup, requestTicket, userID, password,claimDate, memberKey1, memberKey2, doctorCode) });
if (or2 != null) {
StringBuffer sb = new StringBuffer(); sb.append("<OutputResponse><RequestTicket>"); sb.append(or2.getRequestTicket()); sb.append("</RequestTicket><ResponseTicket>"); sb.append(or2.getResponseTicket()); sb.append("</ResponseTicket><ResponseCode>"); sb.append(or2.getResponseCode()); sb.append("</ResponseCode><ResponseMessageEng>"); sb.append(or2.getResponseMessageEng()); sb.append("</ResponseMessageEng><ResponseMessageChi>"); sb.append(or2.getResponseMessageChi()); sb.append("</ResponseMessageChi><TechnicalRemarks>"); sb.append(or2.getTechnicalRemarks()); sb.append("</TechnicalRemarks><InsuredName>"); sb.append(or2.getInsuredName()); sb.append("</InsuredName><GPCopayAmt>"); sb.append(or2.getGPCopayAmt()); sb.append("</GPCopayAmt><GPXMedAmt>"); sb.append(or2.getGPXMedAmt()); sb.append("</GPXMedAmt><SPCopayAmt>"); sb.append(or2.getSPCopayAmt()); sb.append("</SPCopayAmt><SPXMedAmt>"); sb.append(or2.getSPXMedAmt()); sb.append("</SPXMedAmt></OutputResponse>"); return sb.toString(); } else { return null; } } catch (Exception e) { e.printStackTrace(); return null; } }
} 这时候,我们就不用担心安全证书的变化问题和过时失效问题,因为这时我们每次调用时都重新用最新的证书进行了验证,但是这时是以牺牲性能来换取的。
声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
浏览 3924 次