(點選上方公眾號,可快速關註)
來源:袁鳴凱 ,
blog.csdn.net/lifetragedy/article/details/7844589
一、加密保護我們的web service傳輸
在上一天的教程中,我們講了一個簡單的基於” security-constraint”的以指定使用者名稱和密碼來保護一個Web Service以及如何用https對這個web service的通訊過程進行保護。雖然它用https來進行保護了,但是我們拋開https,這個web service之間傳輸的使用者名稱,密碼,資料都是明文的。
在我之間教程中曾經提到過,有一種駭客工具叫作sniffer,或者使用MIM-ATTACK(中介軟體攔截)的方式,也是可以把客戶端的流攔截住並且發往駭客主機的,這樣我們的使用者名稱和密碼就可以被駭客所獲取了。
因此,今天我們要講述的就是如何在web service傳輸時,使得這個使用者名稱和密碼以及相關資料也能被加密。
二、基本概念
我們先來看基本概念,這個基本概念將涉及到PKI的相關領域,請仔細看完這一章,要不然後面你將雲裡霧裡然後我勸你從頭來過,我將參照麻省理工大學的教程-RSA公司出版的“計算機加密與解密原理”,用最實際的例子和最簡化的語言把PKI中最重要的幾個概念給大家說清楚。
這次應該是我們第三次要求生成證書請求,證書,簽名了,挺折騰的!!!
不折騰你們不行,我要把大家折騰的蛋疼,這次折騰過後就徹底明白了。
被折騰著,痛苦著並最後快活著,好了我廢話又多了,下麵開始。
2.1 加密解密的基本概念
我們的加密解密分兩種:
-
對稱加密(Symmetric Cipher)
-
非對稱加密(Asymmetric Cipher)
2.1.1 對稱加密
即採用一個密碼(金鑰)來對一串String進行解密,同樣這個密碼(金鑰)也能對被加密的密文進行解密,至始至終只有一個密碼(金鑰),因此它叫做對稱加密。
2.1.2 非對稱加密
這個是最重要的概念之一
我們知道,對稱加密只有一把金鑰(你可以把這個金鑰看成一個密碼)。而非對稱加密呢?它有2把金鑰,
-
一把我們稱為私鑰即privatekey,一把私鑰可以對應著無數把公鑰,公鑰是可以“散播”的。
-
一把我們稱為公鑰即publickey,一堆公鑰只能對應著僅有的一把私鑰,私鑰是絕對不可以“散播”的。
這兩把金鑰在產生時是被一起產生的,相當於同年同月生一樣,即生成私鑰時也伴隨著生成了公鑰。
下麵公式來了:
公鑰加密,私鑰解密
大家試想一下哈,我有兩把鑰匙,一把是用來專門鎖門的(加密),一把是專門用來開門的(解密)。那麼我用來鎖門的那把key掉了,被其它人撿到了,要不要緊?大不了別人可以鎖我家的門。
但是,如果我用來開門的這麼key掉了?怎麼辦?被人撿到了人家就可以開我家的門進我家了。
因此,公鑰永遠被用來加密,可以有多把被多人持有,而私鑰永遠用來解密且只能主人自己擁有。
公鑰加密,私鑰解密!老老記住,這是永遠的公式,也是真理!
2.1.3 數字簽名
看了上面的“真理”即“公鑰加密,私鑰解密”後有人說了,我偏不信邪我就是要把它們倒過來,好好好!我們一起來看倒過來是什麼樣的,即成了“私鑰加密,公鑰解密”了。但話不能這樣說,真理是不容否定的,但倒過來不是也行?
行,行是行,不過這句話就不能這樣說了。
我們知道,公鑰是可以多把的,私鑰只有一把,因此:
1) 如果我們先把我們的明文用MD5或者SHA1這樣的雜湊演演算法做一個雜湊,得到一堆雜湊值我們稱它為報文。
2) 然後呢我們拿著我們的私鑰來對著這個得到的雜湊碼不管它是MD5還是SHA1,做一個加密運算,就得到了一個“摘要”即Digest。
3) 然後我們把這個摘要和我們的明文一起傳送給接收方。
4) 接收方首先用與傳送方一樣的雜湊函式與接收到的原始明文中計算出這個雜湊計算,得到雜湊值即報文。
5) 接著再用傳送方的公用金鑰來對這個報文附加的數字簽名進行解密,這樣,在接收方手上就會有兩樣東西了。
l 接收方用傳送方的公鑰與所謂的原始明文運算得到的雜湊值或者稱為報文也可稱為摘要即digest。
l 接收方收到的由傳送方發過來的摘要
6) 將這兩要東西,就是兩個摘要,它通常是如下的格式:
0a f5 b0 3f 38 6b 97 9c 08 62 9b 8b df d7 a0 c6 fe 00 12 08
從接收方傳送來的摘要是出自某某某之手!為什麼?把這兩個摘要一比較,完全一致我們就可以說:
舉例來說:
因為我們的公鑰和金鑰在產生時是一對的,Andy保留了私鑰,他把公鑰給了Forest。
If
Forest用這把公鑰和明文得到的摘要如果==Andy用私鑰和明文做了雜湊後發來的摘要
Then
這條訊息一定是Andy發過來的。
除非Andy把他的私鑰交給了其它人並授權其它人代理他來做這個“私鑰簽證”
所以,我們得到另一條公式:
私鑰簽名,公鑰認證
這也是一條真理,不能違背,這條真理也被稱為“數字簽名”,這邊的“認證”也可以稱為“被信任”。
2.1.4 口令保護
我們的private key,public key如果一旦真的出現了private key被丟失的情況下怎麼辦?
不要緊,我們在private key上加一道鎖:
這下成了最右邊那個帶鎖的信封了,是不是,這個帶鎖的信封就是我們的鑰匙袋。
你要拿到我的私鑰就必須要先開啟這個鑰匙帶,開啟這個鑰匙帶你就必須再需要一把鑰匙。
一般這把鑰匙就是一個密碼,我們稱之為“口令”。
來看如下的一個jks的生成來理解吧:
keytool -genkey -alias shnlap93client -keyalg RSA -keysize 1024 -dname “CN=shnlap93, OU=insurance, O=CTS, L=SH, S=SH, C=CN” -keypass bbbbbb -keystore shnlap93client.jks -storepass bbbbbb
JKS檔案就是“公鑰私鑰證書”在一起的一種證書格式。
一般證書如IE中的證書都只帶著公鑰(public key),而jsk是同時帶有公鑰和私鑰的證書。
因此,如果你要得到這對金鑰,你就必須要“解信封”,解開信封時你就要輸入密碼(口令),這邊的口令就是:keypass即bbbbbb六個小寫的b。
2.2 回過頭來看https與證書、公鑰、私鑰的關係
CA證書簽出一張伺服器證書,在簽出簽書時CA使用自己的私鑰,簽完後怎麼叫作這張被簽的證書生效呢?因為這張被簽的證書中含有著CA證書中的“公鑰”。
然後我們產生一個客戶端的證書,該客戶端的證書生成後我們把CA的公鑰也“燒”到客戶端的證書裡。
因此,我們有了下麵這樣的一個關係(見第十三天教程中的3.3小節-需要為我們的Axis2的呼叫客戶端也建立起https中的互信)。
這邊的客戶端jks檔案(帶著RootCA),就是帶著RootCA的公鑰,因為公鑰是可以多把的嗎,因此RootCA可以把自己的公鑰散播給其它人。
於是我們來套“私鑰簽名,公鑰認證”這個公式就得到了:
1) 因為客戶端的jks檔案的公鑰可以“認證”RootCA,即RootCA被客戶端信任。
2) 因為服務端的ks檔案也帶著RootCA的公鑰,可以“認證”RootCA,即RootCA可以被服務端信任。
3) 因此,客戶端可以信任服務端
2.3 使用jks檔案來實現Server-Client間的“公鑰加密,私鑰解密”
上面說了,客戶端擁有RootCA的public key,服務端也擁有RootCA的public key,所以客戶端與服務端可以彼此間也建立起這條信任鏈。
但是,信任不代表它們可以實現“公鑰加密,私鑰解密”。
來看一個例子,這是今天的核心:
需求一、如果我們的客戶端對一個web service進行加密,然後傳到服務端進行解密。
對於需求一來說:
我們需要在客戶端用服務端的公鑰加密欲傳給服務端的訊息,然後在服務端得到客戶端傳過來的加密訊息後我們拿著服務端的私鑰進行解密。
需求二、服務端將傳回的webservice的內容先用客戶端的公鑰加密後傳給客戶端,客戶端用自己的私鑰進行解密。
對於需求二來說:我們在服務端傳回給客戶端訊息之間先用客戶端的公鑰對訊息進行加密,等訊息傳到客戶端的時候,客戶端再拿著客戶端自己的私鑰進行解密。
是不是就可以滿足上述兩個需求了?
於是,根據上面的描述我們來做一件事,即:
-
把客戶端的公鑰燒到服務端的jks檔案裡
-
把服務端的公鑰燒到客戶端的jks檔案裡
即可達到上面兩個需求中的文字描述了,是不是?
三、使用Axis2Rampart實現Web Service安全規範
3.1 Axis2與Rampart
Web Service安全規範其實就是在講一套客戶端與服務端的webservice之間如何進行認證、加密的手段。在Axis2中,是以handler的樣式來實現web service安全規範的。
在舊的Axis版本中,使用的是自己寫handler然後來實現客戶端與服務端之間的web service加密、認證。而到了Axis21.4版即後續版中,這個handler已經不需要我們手工寫了,Axis2為我們提供了一個元件,這個元件叫rampart。
rampart是axis2實現ws-security的一個必須模組。它基於wss4j來完成安全相關的任務,以下是rampart的工作原理:
因為Rampart是基於Handler樣式的,是對handler的一種封裝,它很像一個攔截器,因此有了Rampart我們可以在不需要改動客戶端、服務端的程式碼的情況下只通過修改service端與client端的xml即可實現不同的web service安全規範,而且有了rampart我們只需要提供client端與server端的jks以及相關的“口令”和“使用者名稱(即jks的alias-別名)Rampart就可以自動為我們進行:
加密;
解密;
認證;
而不需要我們再去書寫加密、解密、認證的底層API了,夠方便吧。
3.2 動手來做例子
需求:
實現客戶端訪問一個webservice,彼此間只通過http因此需要對客戶端傳給服務端的message進行加密,服務端得到客戶端傳來的加密的message後解密並把要傳回給客戶端的訊息進行加密傳回給客戶端。客戶端得到服務端傳回後的被加密的訊息後進行解密,並顯示在客戶端。
3.2.1 搭建環境
請下載Rampart1.4(http://axis.apache.org/axis2/java/rampart/download.html)。
解壓後為下麵這樣的目錄結構:
-
把”modules”裡的兩個mar拷入你工程的WEB-INF\modules目錄下;
-
把lib目錄下的jar拷入你工程所在的WEB-INF\lib目錄下並載入入eclipse的classpath路徑中去;
開始實現我們的web service。
3.2.2 Service端
1)org.sky.axis2.security. MyRampartService
package org.sky.axis2.security;
import javax.xml.stream.XMLStreamException;
import org.apache.axiom.om.OMElement;
public class MyRampartService {
public String rampartMethod(String userName) throws XMLStreamException {
return “hello: ” + userName;
}
}
2)service.xml檔案
test rampart service
透過上面的描述我們可以看到,這個Rampart的Service需要對兩個流即:流出(InflowSecurity)與流進( OutflowSecurity)進行攔截。
-
InflowSecurity時
即client端使用service端的公鑰加密後提交上來的資料,我們在In時需要呼叫service端的私鑰進行解密,因此:
Encrypt org.sky.axis2.security.RampartPasswordCB
service.properties
它使用一個實現了PasswordCallBack介面的類來進行呼叫service端的private key進行解密。
前面我們說過了,由於key都由口令保護著:
因此我們先來看這個” org.sky.axis2.security.RampartPasswordCB”類吧:
package org.sky.axis2.security;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import org.apache.ws.security.WSPasswordCallback;
public class RampartPasswordCB 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();
System.out.println(“id====” + id);
if (“shnlap93client”.equals(id)) {
pwcb.setPassword(“bbbbbb”);
} else if (“shnlap93server”.equals(id)) {
pwcb.setPassword(“aaaaaa”);
} else {
System.out.println(“Your are not a authorized user”);
throw new UnsupportedCallbackException(callbacks[i],
“Your are not a authorized user”);
}
}
}
}
要呼叫一個jks檔案的內容需要知道兩大元素即:
-
我們先要知道這個jks的Alias;
-
我們要知道這個jks的保護口令;
比如說我們檢視一個test.jks檔案的內容因該用如下命令:
keytool –v –list–keystore test.jks
然後回車,此時console會提示我們輸入相關的密碼即口令,然後我們就可以看到這個jks檔案的輸出了。
註意:
Jks檔案的Alias名必須小寫。
因此我們回過頭來看上面的這個類
if (“shnlap93client”.equals(id)) {
pwcb.setPassword(“bbbbbb”);
} else if (“shnlap93server”.equals(id)) {
pwcb.setPassword(“aaaaaa”);
} else {
System.out.println(“Your are not a authorized user”);
throw new UnsupportedCallbackException(callbacks[i],
“Your are not a authorized user”);
}
由其是這一段,它就代表著,我們在解密時呼叫service端的jks的私鑰,要呼叫這個私鑰我們必須輸入:
Jks的alias與jks的key的保護口令。
這個類寫完後請放著,我們在書寫客戶端時將使用相同的類(一點程式碼都不需要修改)。
InflowSecurity講完了,我們繼續看下去,來看這個OutflowSecurity
-
OutflowSecurity時
流出時即Service端將要傳回給Client端的內容加密然後輸出,因此OutflowSecurity裡做的應該是加密,對吧?
Encrypt
shnlap93client
org.sky.axis2.security.RampartPasswordCB
service.properties
加密用的是client端的公鑰,那麼Client的公鑰也需要輸入jks的alias,那麼客戶端公鑰的alias就是這個
3)service.properties檔案
最後來看這個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=aaaaaa
org.apache.ws.security.crypto.merlin.file=shnlap93.jks
它位於如下目錄結構:
這個檔案裡描述的就是Service端解密時需要使用的private key的alias與口令。
3.2.3 編寫client端前的準備
在編寫client端前,我們需要生成service端與client端的兩對金鑰,對吧?我們來看下麵的表格:
全部為小寫的哈!!!記住!
還記得我們前面已經做過幾次jks與證書了?
第一次:
為tomcat的ssl我們生成了一個自簽的CA,把CA放到了我們的IE的根信任域,然後生成了一個服務端的證書,並且用CA對這張證書進行簽名,然後把生成的服務端的證書導成了jks,這個jks給了tomcat的server.xml進行配置https時用。相關命令如下:
生成CA的KEY:
openssl genrsa -des3 -out ca.key 1024
從CA的KEY匯出自簽的CA證書:
openssl req -new -x509 -key catest.key -out catest.crt
生成服務端的jks:
keytool -genkey -alias shnlap93server -keyalg RSA -keysize 1024 -dname “CN=shnlap93.cts.com, OU=insurance, O=CTS, L=SH, S=SH, C=CN” -keypass aaaaaa -keystore shnlap93.jks -storepass aaaaaa
透過服務端的jks生成證書請求:
keytool -certreq -alias shnlap93server -sigalg “MD5withRSA” -file shnlap93.csr -keypass aaaaaa -keystore shnlap93.jks -storepass aaaaaa
用CA對服務端的證書請求進行簽名並形成服務端的證書:
openssl x509 -req -in shnlap93.csr -out shnlap93.crt -CA ca.crt -CAkey ca.key -days 3650 -CAcreateserial -sha1 -trustout -CA ca.crt -CAkey ca.key -days 3650 -CAserial ca.srl -sha1 –trustout
先將CA匯入服務端的jks的信任域即:trustcacerts中去:
keytool -import -alias rootca -trustcacerts -file ca.crt -keystore shnlap93.jks -storepass aaaaaa
再把被CA簽了名的正式證書匯入服務端的jks裡
keytool -import -alias shnlap93server -file shnlap93.crt -keystore shnlap93.jks -storepass aaaaaa
第二次:
為了使得我們的Java應用程式也能和tomcat之間形成https的連線,我們生成了一個client端的jks,然後把CA的公鑰匯入了我們的client端的jks的信任域裡即:trustcacert中,使得我們的client端與server端透過共同信任同一個CA的公鑰而達成三角互信關係
相關命令如下:
生成客戶端的jks檔案:
keytool -genkey -alias shnlap93client -keyalg RSA -keysize 1024 -dname “CN=shnlap93, OU=insurance, O=CTS, L=SH, S=SH, C=CN” -keypassbbbbbb -keystore client.jks -storepassbbbbbb
將CA匯入客戶端的信任域即trustcacerts中去:
keytool -import -alias rootca -trustcacerts -file ca.crt -keystore shnlap93client.jks -storepassbbbbbb
第三次:
就是這次,我們需要把
客戶端的公鑰匯入服務端的jks,使得服務端的jks檔案不僅有自己的公鑰與私鑰同時還帶著客戶端的公鑰,服務端的這個jks給服務端用。
服務端的公鑰匯入客戶端的jks,使得客戶端的jks檔案不僅有自己的公鑰與私鑰同時還帶著服務端的公鑰,客戶端的這個jks給客戶端用。
相關命令如下:
第一步:
先把客戶端的jks檔案導成crt,jks導成crt後就會自動把jks裡的public key帶入到這個crt檔案,因此也可以把jks導成crt看成是public key的匯出。
第二步:
再把服務端的jks檔案導成crt
第三步:
兩個public key都有了,然後互導!!
先將服務端的公鑰匯入客戶端的jks檔案裡,由於此時要對client端的jks檔案內容進行修改,因此係統會提示輸入密碼,這個密碼就是client端的jks檔案的口令即:bbbbbb六個b。
再將客戶端的公鑰匯入服務端的jks檔案裡,由於此時要對server端的jks檔案內容進行修改,因此係統會提示輸入密碼,這個密碼就是server端的jks檔案的口令即:aaaaaa六個a。
好了,兩個jks檔案shnlap93.jks與shnlap93client.jks檔案都有了,把shnlap93.jks檔案放入工程的src目錄下使得工程在編譯時會把這個shnlap93.jks編譯進入我們的執行時的classpath
然後下麵開始我們來編寫我們的client端了。
3.2.4 編寫client端
我們新建一個eclipse工程,一般Java工程即可。
工程結構如下:
-
把Rampart1.4中的modules目錄裡的.mar檔案都拷入client工程的modules目錄下;
-
把Rampart1.4中的lib目錄裡的jar檔案與axis2的lib目錄下的jar都拷入client工程的lib目錄下並載入入classpath;
-
把我們在3.2.3小節中生成的client端的jks檔案即shnlap93client.jks檔案放入client 工程的src目錄下。
-
把我們在3.2.2小節中生成的” org.sky.axis2.security.RampartPasswordCB”檔案也拷入client工程。
1)org.sky.axis2.security. MyRampartServiceClient
package org.sky.axis2.security;
import javax.xml.namespace.QName;
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;
public class MyRampartServiceClient {
private static EndpointReference targetEPR = new EndpointReference(
“http://localhost:8080/Axis2Service/services/MyRampartService”);
private String getAxis2ConfPath() {
StringBuilder confPath = new StringBuilder();
confPath.append(this.getClass().getResource(“/”).getPath());
confPath.append(“repository”);
return confPath.toString();
}
private String getAxis2ConfFilePath() {
String confFilePath = “”;
confFilePath = getAxis2ConfPath() + “/axis2.xml”;
return confFilePath;
}
public void testMyRampartService() {
Options options = new Options();
options.setAction(“urn:rampartMethod”);
options.setTo(targetEPR);
ServiceClient sender = null;
String confFilePath = “”;
String confPath = “”;
try {
confPath = this.getAxis2ConfPath();
confFilePath = getAxis2ConfFilePath();
System.out.println(“confPath======” + confPath);
System.out.println(“confFilePath====” + confFilePath);
ConfigurationContext configContext = ConfigurationContextFactory
.createConfigurationContextFromFileSystem(confPath,
confFilePath);
sender = new ServiceClient(configContext, null);
sender.setOptions(options);
OMFactory fac = OMAbstractFactory.getOMFactory();
OMNamespace omNs = fac.createOMNamespace(
“http://security.axis2.sky.org”, “”);
OMElement callMethod = fac.createOMElement(“rampartMethod”, omNs);
OMElement nameEle = fac.createOMElement(“name”, omNs);
nameEle.setText(“Wang Clare”);
callMethod.addChild(nameEle);
OMElement response = sender.sendReceive(callMethod);
System.out.println(“response====>” + response);
System.out.println(response.getFirstElement().getText());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (sender != null)
sender.disengageModule(“addressing”);
try {
sender.cleanup();
} catch (Exception e) {
}
}
}
public static void main(String[] args) {
MyRampartServiceClient rampartServiceClient = new MyRampartServiceClient();
rampartServiceClient.testMyRampartService();
}
}
這是一個標準的普通的web service訪問客戶端,沒啥花頭,唯一需要註意的就是:
ConfigurationContext configContext = ConfigurationContextFactory
.createConfigurationContextFromFileSystem(confPath,
confFilePath);
sender = new ServiceClient(configContext, null);
這個configContext需要兩個引數:
第一個引數指向:工程的src目錄下的repository檔案夾;
第二個引數指向:工程的src目錄下的repository檔案夾內的axis2.xml;
2)工程的src\repository\axis2.xml檔案
這個檔案可以直接從axis2下載解壓包中的conf目錄下的axis2.xml檔案的內容複製過來,複製過來後在裡面新增如下內容(紅色加粗的部分為我們手工新增的內容):
Encrypt
shnlap93server
client.properties
有了前面service.xml檔案中的講述,這個內容因該不難看懂了吧?
它無非就是:
在In時,需要把從服務端用客戶端的公鑰加密後的資料,在客戶端用客戶端的私鑰進行解密;
在Out時,需要用服務端的公鑰對資料進行加密再傳給服務端。
3) 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=bbbbbb
org.apache.ws.security.crypto.merlin.file=shnlap93client.jks
它和service端的service.properties檔案內容一樣,是對client 端的私鑰即shnlap93client.jks的口令與相關的alias的描述。
然後我們把service端佈署入tomcat並啟動得到我們的Web Service
我們先試著用SOAP UI來呼叫:
看到右邊的輸出沒?不成功,為什麼?
WSDoAllReceiver: Incoming message does not contain requiredSecurity essay-header
我們的Web Service已經是使用者名稱和密碼保護並且我們的request和response都必須是加密的了。
現在我們來在eclipse裡執行我們的客戶端:
看到輸出沒?呼叫成功。
四、換一種Web Service Security規範來試試同樣的例子
前面在3.1小節中我們說過了:
Rampart是攔截器原理,因此有了Rampart我們可以在不需要改動客戶端、服務端的程式碼的情況下只通過修改service端與client端的xml即可實現不同的web service安全規範
我們前面這個例子用的是” Encrypting messages”方式。
我們現在來換成另一種更安全的叫” Sign and encrypt messages”的方式來對我們的web service的服務端與客戶端進行安全傳輸:
4.1 換service端的配置
service.xml檔案
Timestamp Signature
service.properties
4.2 換client端的配置
axis2.xml檔案
Timestamp Signature
shnlap93client
client.properties
org.sky.axis2.security.RampartPasswordCB
DirectReference
好了,除這兩個xml配置檔案中的內容換一下,其它的程式碼,properties,jks檔案都保持不動,再重新來執行我們的client端的程式:
成功,真棒!!!結束今天的教程,結束Axis2之旅。
有了這5天的Axis2教程,已經將Axis2的基本知識和概念介紹給了大家,大家透過這5天的學習再結合Axis2自帶的Sample相信可以完全掌握Axis2了。
系列
看完本文有收穫?請轉發分享給更多人
關註「ImportNew」,提升Java技能