<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Terry.Li-彬

    虛其心,可解天下之問;專其心,可治天下之學;靜其心,可悟天下之理;恒其心,可成天下之業。

      BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
      143 隨筆 :: 344 文章 :: 130 評論 :: 0 Trackbacks
    Web 服務安全性相關技術和開發工具

    Web 服務安全性規范是一套可以幫助 Web 服務開發者保證 SOAP 消息交換的安全的機制。WS-Security 特別描述了對現有的 SOAP 消息傳遞的增強,從而通過對 SOAP 消息應用消息完整性、消息機密性和單消息認證提供了保護級別。這些基本機制可以通過各種方式聯合,以適應構建使用多種加密技術的多種安全性模型。

    圍繞Web服務的安全,有很多相關的技術,比如WS-Security,WS-Trace等,另外,還有以下相關技術:

    XML Digital Signature(XML數字簽名)
    XML Encryption (XML加密)
    XKMS (XML Key Management Specification)
    XACML (eXtensible Access Control Markup Language)
    SAML (Secure Assertion Markup Language)
    ebXML Message Service Security
    Identity Management & Liberty Project
    由于本文是一個實例性文章,故不對WS-Security做詳細的探討,你可以在develperWorks Web 服務安全專題找到許多相關資料(見參考資料)。

    Trust Services Integration Kit提供了一個WS-Security實現。你可以從http://www.xmltrustcenter.org獲得相關庫文件,分別是wssecurity.jar和tsik.jar。wssecurity.jar中包含一個WSSecurity類,可以使用它來對XML進行數字簽名和驗證,加密與解密。

    下面我們使用WS-Security來對SOAP消息進行數字簽名,然后再進行驗證。



     


     回頁首
     



    SOAP消息的簽名和驗證

    使用WSSecurity對SOAP消息數字簽名

    在對SOAP消息進行簽名前,首先生成一個keystore。keystore包含了進行數字簽名所需要的身份信息。通過以下批處理腳本來創建keystore:


    例程1 創建keystore(server.keystore)

    set SERVER_DN="CN=hellking-Server, OU=huayuan, O=huayuan, L=BEIJINGC, S=BEIJING, C=CN"
    set KS_PASS=-storepass changeit
    set KS_TYPE=-storetype JKS
    set KEYINFO=-keyalg RSA
    #生成服務器端keystore。
    keytool -genkey -dname %SERVER_DN% %KS_PASS% %KS_TYPE% -keystore
    server.keystore %KEYINFO% -keypass changeit
     


    SignAndVerifySoap類中包含了一個對XML進行簽名的方法,它就是sign(),這個方法將對SOAP消息進行簽名,然后輸出和WS-Security兼容的SOAP消息。下面我們看具體代碼。


    例程2 對SOAP消息簽名

        package com.hellking.study.webservice;
    import com.verisign.messaging.WSSecurity;
    ...
    public class SignAndVerifySoap {
       
        final String KEY_STORE = "server.keystore";
        final String SOTE_PASS = "changeit";
        final String KEY_ALIAS="mykey";
        final String TARGET_FILE="signed.xml";//簽名后的SOAP消息
        final String SOURE_FILE="source.xml";//簽名前的SOAP消息
        final String KEY_TYPE="JKS";
       
        /**
         *對xml進行簽名
         */
        public void sign()
        {   

            try
            {
                System.out.println("開始對SOAP消息進行簽名,使用的密匙庫:" + KEY_STORE + "\n");
           
            // 獲得私有key和相關證書,請參考JAVA安全編程相關書籍
            FileInputStream fileInputStream = new FileInputStream(KEY_STORE);
            System.out.println(java.security.KeyStore.getDefaultType());
            java.security.KeyStore store = java.security.KeyStore.getInstance(KEY_TYPE);
            store.load(fileInputStream, SOTE_PASS.toCharArray());
            PrivateKey key = (PrivateKey)store.getKey(KEY_ALIAS, SOTE_PASS.toCharArray());
            X509Certificate certification = (X509Certificate)store.getCertificate(KEY_ALIAS);

            // 讀取XML源文件到文檔中
            Document source = readFile(SOURE_FILE);
            SigningKey signingKey = SigningKeyFactory.makeSigningKey(key);
            KeyInfo keyInfo = new KeyInfo();
            keyInfo.setCertificate(certification);

            WSSecurity wsSecurity = new WSSecurity();

            wsSecurity.setPreferredNamespace("http://schemas.xmlsoap.org/ws/2003/06/secext");
            //對SOAP消息進行簽名
            wsSecurity.sign(source, signingKey, keyInfo);
            // 保存簽名后的SOAP消息   
            writeFile(source, new FileOutputStream(TARGET_FILE));
            System.out.println("把簽名后的文件寫入: " + TARGET_FILE + ",請查看結果!");
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }       
        }
         


    在執行此程序前,請把wssecurity.jar、source.xml和tsik.jar設置到類路徑環境變量中。簽名前的SOAP為:


    例程3 簽名前的SOAP消息(source.xml)

    <?xml version="1.0" encoding="UTF-8"?>
    <soapenv:Envelope
        xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
     <soapenv:Body>
      <ns1:getTax soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
    xmlns:ns1="http://hellking.webservices.com/">
       <op1 xsi:type="xsd:double">5000.0</op1>
      </ns1:getTax>
     </soapenv:Body>
    </soapenv:Envelope>
     


    簽名后的SOAP消息如例程4所示。


    例程4 簽名后的SOAP消息(signed.xml)

    <?xml version="1.0" encoding="UTF-8"?>
    <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
     <soapenv:Header>
    <wsse:Security xmlns:wsse="http://schemas.xmlsoap.org/ws/2003/06/secext">
    <wsse:BinarySecurityToken EncodingType="wsse:Base64Binary"
    ValueType="wsse:X509v3" wsu:Id="wsse-ee805a80-cd95-11d8-9cf9-fd6213c0f8be"
    xmlns:wsu="http://schemas.xmlsoap.org/ws/2003/06/utility">MIICUjCCAbsCBEDB0GIwDQYJKoZIhvcNAQE…VkTkPw==
    </wsse:BinarySecurityToken>
    <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
    <ds:SignedInfo>
    <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
    <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
    <ds:Reference URI="#wsse-ee5308f0-cd95-11d8-9cf9-fd6213c0f8be">
    <ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
    </ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
    <ds:DigestValue>ZjRVnI2g7kcX0h9r4JtiltpYQPA=</ds:DigestValue></ds:Reference>
    <ds:Reference URI="#wsse-ee4e4e00-cd95-11d8-9cf9-fd6213c0f8be">
    <ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
    </ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
    <ds:DigestValue>moZ0d+8mH1kfNw0VEK39V0Td9EM=</ds:DigestValue>
    </ds:Reference>
    </ds:SignedInfo>
    <ds:SignatureValue>fPpYrf0uNP8W2XVVIQNc3OQt2Wn90M/0uJ0dDZTNRR0NxBBBX36wSXt7NfI5Fmh4ru44Wk34EGI7mqMAE5O0
    /wtIlFRJt3zAvA6k3nhgcYj6tn/9kZwwxh1RkFTfTX9xdQ6Xn+P6m+YBm1YEEcTWkJd7XcxdyDEns2kYOhONx1U=
    </ds:SignatureValue>
    <ds:KeyInfo><wsse:SecurityTokenReference>
    <wsse:Reference URI="#wsse-ee805a80-cd95-11d8-9cf9-fd6213c0f8be"/>
    </wsse:SecurityTokenReference>
    </ds:KeyInfo>
    </ds:Signature></wsse:Security>
    <wsu:Timestamp xmlns:wsu="http://schemas.xmlsoap.org/ws/2003/06/utility">
    <wsu:Created wsu:Id="wsse-ee4e4e00-cd95-11d8-9cf9-fd6213c0f8be">2004-07-04T08:41:23Z</wsu:Created>
    </wsu:Timestamp></soapenv:Header>
    <soapenv:Body wsu:Id="wsse-ee5308f0-cd95-11d8-9cf9-fd6213c0f8be"
        xmlns:wsu="http://schemas.xmlsoap.org/ws/2003/06/utility">
      <ns1:getTax soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
          xmlns:ns1="http://hellking.webservices.com/">
       <op1 xsi:type="xsd:double">5000.0</op1>
      </ns1:getTax>
     </soapenv:Body>
    </soapenv:Envelope>
     


    簽名后的SOAP消息中,頭部包含了簽名信息以及驗證SOAP消息所需要的key。<SignedInfo> </SignedInfo> 描述了已簽署的消息內容。<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/> 指出了簽名算法(Signature Method Algorithm)。這個算法被用來將規范算法的輸出轉換成簽名值(Signature Value)。Key Info 元素包含的部分就是數字證書本身。

    對簽名的SOAP消息進行驗證

    對SOAP消息進行驗證就是使用keystore的信息生成TrustVerifier對象,然后調用WSSecurity的verify方法進行驗證。


    例程5 驗證簽名后的SOAP消息

        /**
         *驗證已經簽名的SOAP消息
         */
        public void verify()
        {
            try
            {
                System.out.println("開始檢驗SOAP消息,使用的密匙庫:" + KEY_STORE + "\n");
           
            // 獲得私有key和相關證書,請參考JAVA安全編程相關書籍
            FileInputStream fileInputStream = new FileInputStream(KEY_STORE);

            java.security.KeyStore store = java.security.KeyStore.getInstance(KEY_TYPE);
            store.load(fileInputStream, SOTE_PASS.toCharArray());
       
            // 讀取XML源文件到文檔中
            Document source = readFile(TARGET_FILE);
            org.xmltrustcenter.verifier.TrustVerifier verifier =
                new org.xmltrustcenter.verifier.X509TrustVerifier(store);

            WSSecurity wsSecurity = new WSSecurity();

            com.verisign.messaging.MessageValidity[] resa =
         wsSecurity.verify(source,verifier,null,null);
           System.out.println("檢驗結果:");
           for (int len = 0; len < resa.length; len++){
          System.out.println("result[" + len + "] = " + (resa[len].isValid()?"驗證通過":"驗證不通過"));
        }
        }

        catch(Exception e)
        {
            e.printStackTrace();
        }
    }
     


    執行SignAndVerifySoap的verify方法,可以看到類似以下的結果。


    圖1 對SOAP消息進行驗證
     


     


     回頁首
     



    在AXIS下實現WS-Security的應用框架

    待開發的應用開發框架基于Handler實現,將達到以下目標:此框架基于JAX-RPC環境下實現WS-Security應用,它可以部署到任何需要實現WS-Security的axis環境下的Web服務應用中,同時具體的應用程序不做任何編碼修改。

    由于此基于Handler實現,我們有必要回顧一下Handler的一些基礎知識。

    SOAP消息Handler能夠訪問代表RPC請求或者響應的SOAP消息。在JAX-RPC技術中,SOAP消息Handler可以部署在服務端,也可以在客戶端使用。

    SOAP消息Handler非常像Servlet技術中的Filter,它們共同的特點是請求發送到目標前,Handler/Filter可以截取這些請求,并對請求做一些處理,從而達到一些輔助的功能。多個Handler可以組成一個Handler鏈,鏈上的每個Handler都完成某個特定的任務。比如有的Handler進行權限驗證,有的Handler進行日志處理等。關于Handler更詳細的介紹,請參考本系列文章《 J2EE Web服務開發系列之六: 使用Handler來增強Web服務的功能》。

    實現原理

    圖2是此例子具體實現原理圖。


    圖2 Handler結合WSSecurity實現Web服務安全的工作原理
     

    處理流程如下:

    1、 客戶端(WSSClient)發出調用Web服務請求;

    2、 客戶端Handler(WSSecurityClientHandler)截獲請求的SOAP消息;

    3、 客戶端Handler對截獲的SOAP消息進行數字簽名(使用client.keystore作為簽名依據);

    4、 客戶端Handler對簽名后的SOAP消息進行加密(使用RSA算法加密);

    5、 被加密的SOAP消息通過互聯網傳送到目標Web服務端口;

    6、 服務器端Handler(WSSecurityServerHandler)截獲加密的SOAP消息;

    7、 服務器端Handler對加密的SOAP消息進行解密;

    8、 服務器端Handler對SOAP消息進行身份驗證(server.truststore包含了所信任的身份信息),如果驗證不通過,將拋出異常;

    9、 服務器端Handler刪除被解密后的SOAP消息中與WS-Security相關的元素;

    10、 解密后的原始SOAP消息被發送到目標Web服務端口(如TaxService);

    11、 目標Web服務對Web服務請求進行處理,然后返回響應的SOAP消息;
    12、 服務器端Handler截獲響應的SOAP消息;

    13、 服務器端Handler對截獲的SOAP消息進行數字簽名(使用server.keystore作為簽名依據);

    14、 服務器端Handler對簽名后的SOAP消息進行加密(使用RSA算法加密);

    15、 被加密的SOAP消息通過互聯網傳送到目客戶端;

    16、 客戶端Handler截獲加密的SOAP消息;

    17、 客戶端Handler對加密的SOAP消息進行解密;

    18、 客戶端Handler對SOAP消息進行身份驗證(client.truststore包含了所信任的身份信息),如果驗證不通過,將拋出異常;

    19、 客戶端Handler刪除被解密后的SOAP消息中與WS-Security相關的元素;

    20、 被解密后的SOAP消息發送到目標客戶端,客戶端輸出調用結果。

    從上面可以看出,在一個SOAP調用回合中,要對SOAP消息進行四次處理。基本上都是"簽名'加密'解密'驗證"的過程。

    創建相關密匙庫

    客戶端和服務端都有相關的密匙庫,其中:

    client.keystore:客戶端自身的身份信息;
    client.truststore:客戶端所信任的身份信息,在此例中也就是包含了服務器的身份信息;
    server.keystore:服務器自身的身份信息;
    server.truststore:服務器所信任的身份信息(即客戶端身份信息)。
    你可以使用以下的批處理腳本創建上面四個密匙庫。


    例程6 創建相關密匙庫(gen-cer-store.bat)

    set SERVER_DN="CN=hellking-Server, OU=huayuan, O=huayuan, L=BEIJINGC, S=BEIJING, C=CN"
    set CLIENT_DN="CN=hellking-Client, OU=tsinghua, O=tsinghua, L=BEIJING, S=BEIJING, C=CN"
    set KS_PASS=-storepass changeit
    set KEYINFO=-keyalg RSA
    #生成server.keystore。
    keytool -genkey -dname %SERVER_DN% %KS_PASS% -keystore server.keystore %KEYINFO% -keypass changeit
    #從server.keystore導出數字證書。
    keytool -export -file test_axis.cer %KS_PASS% -keystore server.keystore
    #從服務器的數字證書導出到客戶端信任的truststore中。
    keytool -import -file test_axis.cer %KS_PASS% -keystore client.truststore -alias serverkey -noprompt

    #生成client.keystore。
    keytool -genkey -dname %CLIENT_DN% %KS_PASS% -keystore client.keystore %KEYINFO% -keypass changeit
    #從client.keystore導出數字證書。
    keytool -export -file test_axis.cer %KS_PASS% -keystore client.keystore
    #從客戶端的數字證書導出到服務器信任的truststore中。
    keytool -import -file test_axis.cer %KS_PASS% -keystore server.truststore -alias clientkey -noprompt

    #end
     


    簽名、加密、解密、身份驗證的實現

    對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腳本,修改了部分環境變量參數后,才能執行。
    posted on 2007-11-24 15:01 禮物 閱讀(1279) 評論(0)  編輯  收藏 所屬分類: webservice
    主站蜘蛛池模板: 女性无套免费网站在线看| 亚洲成_人网站图片| 亚洲国产激情一区二区三区| 天天影视色香欲综合免费| 美女网站在线观看视频免费的 | 四虎影视在线看免费观看| 亚洲一区二区三区亚瑟| 亚洲国产成人片在线观看| 亚洲AV无码一区二区三区国产| 免费a级毛片无码a∨蜜芽试看| 91精品视频在线免费观看| 青青操免费在线观看| 人妻巨大乳hd免费看| 亚洲1区2区3区精华液| 亚洲一区二区三区丝袜| 亚洲AV无码乱码麻豆精品国产| 18亚洲男同志videos网站| 久久久久久a亚洲欧洲AV| 亚洲小说区图片区另类春色| 亚洲精品偷拍视频免费观看| av无码东京热亚洲男人的天堂| 免费jjzz在线播放国产| 特级淫片国产免费高清视频| 成年人免费观看视频网站| 成人免费毛片内射美女-百度| 国产成人yy免费视频| 五月婷婷综合免费| 青青青国产在线观看免费网站 | 久久精品蜜芽亚洲国产AV| 亚洲精品高清视频| 久久亚洲精品国产精品黑人| 亚洲AV无码码潮喷在线观看| 国产亚洲人成网站观看| 亚洲av无码国产精品色午夜字幕| 亚洲av无码一区二区三区网站| 国产亚洲综合色就色| 亚洲综合无码一区二区| 亚洲精品成人网站在线播放| 亚洲人成电影青青在线播放| 国产亚洲精aa在线看| 国产精品亚洲va在线观看|