`

Java Axis-1.4 调用 .net webserice(手动下载证书和程序自动装载证书))

阅读更多

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;

}

}

}


这时候,我们就不用担心安全证书的变化问题和过时失效问题,因为这时我们每次调用时都重新用最新的证书进行了验证,但是这时是以牺牲性能来换取的。
分享到:
评论
1 楼 xinglianxlxl 2018-03-01  
对我有用,谢谢

相关推荐

Global site tag (gtag.js) - Google Analytics