目錄
1.發(fā)布web服務(wù)
2.發(fā)布Web服務(wù)使用Handler來(lái)增強(qiáng)Web服務(wù)的功能
3.建立安全的AXIS服務(wù)(上)
4.建立安全的AXIS服務(wù)(下)
5.在AXIS服務(wù)間傳遞JavaBean及其安全解決
AXIS學(xué)習(xí)筆記(一)
ronghao100 原創(chuàng)
前天頭告訴我用SOAP WEB服務(wù)開(kāi)發(fā)一個(gè)客戶程序,用來(lái)與企業(yè)內(nèi)部的ERP進(jìn)行交互,。晚上趕快找相關(guān)的資料猛看,,總算對(duì)SOAP有了一定的認(rèn)識(shí),。干程序員這行真不容易,好象得不停地學(xué)習(xí)新東西,,一不小心就被淘汰:(不過(guò)學(xué)習(xí)也是個(gè)很有意思的事情,。好了,廢話少說(shuō),,讓我們開(kāi)始吧,。
一、軟件環(huán)境
1,、axis-1_2 (從apache網(wǎng)站下載最新axis-bin-1_2.zip解壓即可)
2,、Tomcat5.0
3、JDK5.0
二,、相關(guān)配置
1,、在你的%TOMCAT_HOME%\common\lib下需要加入三個(gè)包 activation.jar、mail.jar,、tools.jar
2,、環(huán)境變量設(shè)置
AXIS_HOME 即axis-bin-1_2.zip解壓的目錄(我的是在F:\soap\axis-1_2)
AXIS_LIB 即 %AXIS_HOME%\lib
AXISCLASSPATH 即 %AXIS_LIB%\axis.jar;%AXIS_LIB%\commons-discovery-0.2.jar;%AXIS_LIB%\commons-logging-1.0.4.jar;%AXIS_LIB%\jaxrpc.jar;%AXIS_LIB%\saaj.jar;%AXIS_LIB%\log4j-1.2.8.jar;也就是把%AXIS_LIB%下所用JAR文件都導(dǎo)入
三、實(shí)驗(yàn)一下
在%AXIS_HOME%\webapps下找到axis文件夾,,將其整個(gè)拷貝到%TOMCAT_HOME%\webapps下,,啟動(dòng)
Tomcat,打開(kāi)瀏覽器訪問(wèn)http://localhost:8080/axis/,出現(xiàn)以下頁(yè)面說(shuō)明你配置成功了,。很簡(jiǎn)單吧:)
四,、發(fā)布我們的第一個(gè)程序
第一個(gè)程序簡(jiǎn)單的返回HELLO WORLD!
HelloWorld.java
public class HelloWorld {
public String sayHello()
{
return "HELLO WORLD!";
}
}
我們的第一種發(fā)布方式:
將HelloWorld.java拷貝到%TOMCAT_HOME%\webapps\axis下,,然后將其改名為HelloWorld.jws,這樣AXIS就自然將其發(fā)布了?,F(xiàn)在寫個(gè)客戶端程序訪問(wèn)一下:
TestClient.java
import org.apache.axis.client.Call;
import org.apache.axis.client.Service;
import javax.xml.rpc.ParameterMode;
public class TestClient
{
public static void main(String [] args) throws Exception {
String endpoint = "http://localhost:" +"8080"+ "/axis/HelloWorld.jws";//指明服務(wù)所在位置
Service service = new Service(); //創(chuàng)建一個(gè)Service實(shí)例,注意是必須的,!
Call call = (Call) service.createCall();//創(chuàng)建Call實(shí)例,,也是必須的!
call.setTargetEndpointAddress( new java.net.URL(endpoint) );//為Call設(shè)置服務(wù)的位置
call.setOperationName( "sayHello" );//注意方法名與HelloWorld.java中一樣??!
String res = (String) call.invoke( new Object[] {} );//返回String,沒(méi)有傳入?yún)?shù)
System.out.println( res );
}
}
我的測(cè)試是在jbuilder2005中,,注意項(xiàng)目中要導(dǎo)入其自帶的AXIS包(當(dāng)然應(yīng)該把其中JAR文件替換一下),,可以看到程序返回了 "HELLO WORLD!"
可以看到在AXIS里發(fā)布服務(wù)其實(shí)是一件很容易的事,這是因?yàn)檫@個(gè)服務(wù)很簡(jiǎn)單的原因:)下面我們介紹第二種發(fā)布方式,,這是常用的,。
我們的第二種發(fā)布方式:
1、將HelloWorld.java編譯成HelloWorld.class,放到%TOMCAT_HOME%\webapps\axis\WEB-INF\classes
下
2、在%TOMCAT_HOME%\webapps\axis\WEB-INF下新建deploy.wsdd文件,,即SOAP服務(wù)發(fā)布描述文件
deploy.wsdd
<deployment xmlns="http://xml./axis/wsdd/" xmlns:java="" target="_blank">http://xml./axis/wsdd/providers/java">
<service name="HelloWorld" provider="java:RPC">
<parameter name="className" value="HelloWorld"/>
<parameter name="allowedMethods" value="sayHello"/>
</service>
</deployment>
在DOS下轉(zhuǎn)換目錄到%TOMCAT_HOME%\webapps\axis\WEB-INF,,命令:
java -cp %AXISCLASSPATH% org.apache.axis.client.AdminClient deploy.wsdd
你會(huì)發(fā)現(xiàn)目錄下多了一個(gè)server-config.wsdd文件,這就是AXIS的配置文件,,以后所有的服務(wù)發(fā)布描述都會(huì)在里面找到,。(當(dāng)然,你可以直接修改它,,不用再寫deploy.wsdd)然后打開(kāi)瀏覽器http://localhost:8080/axis/servlet/AxisServlet,,你就會(huì)看到你的服務(wù)已發(fā)布
注意:可以直接寫server-config.wsdd,如下:
<deployment xmlns="http://xml./axis/wsdd/" xmlns:java="http://xml./axis/wsdd/providers/java">
<handler type="java:org.apache.axis.handlers.http.URLMapper" name="URLMapper"/>
<service name="myService" provider="java:RPC">
<parameter name="className" value="com.service.myService"/>
<parameter name="allowedMethods" value="getusername"/>
</service>
<transport name="http">
<requestFlow>
<handler type="URLMapper"/>
</requestFlow>
</transport>
</deployment>
同樣用客戶端程序訪問(wèn)一下:(注意和上邊的差別!?。?/p>
HelloClient.java
import org.apache.axis.client.Call;
import org.apache.axis.client.Service;
public class HelloClient
{
public static void main(String [] args) throws Exception {
String endpoint = "http://localhost:" +"8080"+ "/axis/services/HelloWorld";//注意,!差別僅僅在這里!,!
Service service = new Service();
Call call = (Call) service.createCall();
call.setTargetEndpointAddress( new java.net.URL(endpoint) );
call.setOperationName("sayHello" );
String res = (String) call.invoke( new Object[] {} );
System.out.println( res );
}
}
好了,,相信你對(duì)AXIS已有了大致的了解。接下來(lái)將會(huì)涉及到傳參數(shù),、JAVABEAN對(duì)象,,及AXIS的安全問(wèn)題,下次再說(shuō)吧:)也歡迎和我,一個(gè)快樂(lè)的JAVA程序員,,聯(lián)系:)[email protected]
第三種方式:
1、編寫服務(wù)端程序server,,SayHello.java,,編譯server.SayHello.java
package server;
public class SayHello
{
public String getName(String name)
{
return "hello "+name;
}
}
2、編寫wsdd文件
deploy.wsdd文件內(nèi)容如下:
<deployment xmlns="http://xml./axis/wsdd/" xmlns:java="http://xml./axis/wsdd/providers/java">
<service provider="java:RPC">
<parameter value="server.SayHello.getName"/>
<parameter value="*"/>
</service>
</deployment>
3,、發(fā)布服務(wù):
編輯一個(gè)deploy.bat,,Axis_Lib為axis.jar路徑。內(nèi)容如下:
set Axis_Lib=D:\workspace\test\WEB-INF\lib
set Java_Cmd=java -Djava.ext.dirs=%Axis_Lib%
set Axis_Servlet=http://localhost:8080/test/servlet/AxisServlet
%Java_Cmd% org.apache.axis.client.AdminClient -l%Axis_Servlet% deploy.wsdd
執(zhí)行這個(gè)批處理文件,,這時(shí)候,,如果提示成功的話,訪問(wèn)http://localhost:8080/test/services 就會(huì)顯示服務(wù)列表,。
4,、生成客戶端client stub文件
在瀏覽器上訪問(wèn)服務(wù)器端的服務(wù),可以下載到WSDL文件,,通過(guò)Axis的相關(guān)工具,,可以自動(dòng)從WSDL文件中生成Web Service的客戶端代碼。
編寫一個(gè)WSDL2Java.bat文件,,其內(nèi)容如下:
set Axis_Lib=D:\workspace\test\WEB-INF\lib
set Java_Cmd=java -Djava.ext.dirs=%Axis_Lib%
set Output_Path=D:\workspace\test\src
set Package=server.SayHello
%Java_Cmd% org.apache.axis.wsdl.WSDL2Java -o%Output_Path% -p%Package% SayHello.wsdl
執(zhí)行這個(gè)批處理文件就可以生成client stub.
生成的stub client文件列表為:SayHello.java,,SayHelloService.java,SayHelloServiceLocator.java,SayHelloSoapBindingStub.java .
5,、編寫客戶端程序,,編譯并執(zhí)行
下面是一段junit測(cè)試客戶端代碼。
import java.net.URL;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
public class TestWSClient extends TestCase {
public TestWSClient(String string) {
super(string);
}
public void SayHelloClient() throws Exception {
SayHelloService service = new SayHelloServiceLocator();
SayHello_PortType client = service.getSayHello() ;
String retValue = client.getName("clientname");
System.out.println(retValue);
}
public static Test suite() {
TestSuite suite = new TestSuite();
suite.addTest(new TestWSClient("SayHelloClient"));
return suite;
}
}
++++++++++++++++++++++++++++++++++++++
AXIS學(xué)習(xí)筆記(二)使用Handler來(lái)增強(qiáng)Web服務(wù)的功能
Handler的基本概念
J2EE Web 服務(wù)中的Handler技術(shù)特點(diǎn)非常像Servlet技術(shù)中的Filter,。我們知道,,在Servlet中,當(dāng)一個(gè)HTTP到達(dá)服務(wù)端時(shí),,往往要經(jīng)過(guò)多個(gè)Filter對(duì)請(qǐng)求進(jìn)行過(guò)濾,,然后才到達(dá)提供服務(wù)的Servlet,這些Filter的功能往往是對(duì)請(qǐng)求進(jìn)行統(tǒng)一編碼,,對(duì)用戶進(jìn)行認(rèn)證,,把用戶的訪問(wèn)寫入系統(tǒng)日志等。相應(yīng)的,,Web服務(wù)中的Handler通常也提供一下的功能:
對(duì)客戶端進(jìn)行認(rèn)證,、授權(quán);
把用戶的訪問(wèn)寫入系統(tǒng)日志,;
對(duì)請(qǐng)求的SOAP消息進(jìn)行加密,,解密;
為Web Services對(duì)象做緩存,。
SOAP消息Handler能夠訪問(wèn)代表RPC請(qǐng)求或者響應(yīng)的SOAP消息,。在JAX-RPC技術(shù)中,SOAP消息Handler可以部署在服務(wù)端,,也可以在客戶端使用,。
下面我們來(lái)看一個(gè)典型的SOAP消息Handler處理順序:
某個(gè)在線支付服務(wù)需要防止非授權(quán)的用戶訪問(wèn)或者撰改服務(wù)端和客戶端傳輸?shù)男畔ⅲ瑥亩褂孟⒄∕essage Digest)的方法對(duì)請(qǐng)求和響應(yīng)的SOAP消息進(jìn)行加密,。當(dāng)客戶端發(fā)送SOAP消⑹保?突Ф說(shuō)?andler把請(qǐng)求消息中的某些敏感的信息(如信用卡密碼)進(jìn)行加密,,然后把加密后的SOAP消息傳輸?shù)椒?wù)端;服務(wù)端的SOAP消息Handler截取客戶端的請(qǐng)求,,把請(qǐng)求的SOAP 消息進(jìn)行解密,,然后把解密后的SOAP消息派發(fā)到目標(biāo)的Web服務(wù)端點(diǎn)。
Apache axis是我們當(dāng)前開(kāi)發(fā)Web服務(wù)的較好的選擇,,使用axisWeb服務(wù)開(kāi)發(fā)工具,,可以使用Handler來(lái)對(duì)服務(wù)端的請(qǐng)求和響應(yīng)進(jìn)行處理。典型的情況下,,請(qǐng)求傳遞如圖1所示,。
圖1 SOAP消息的傳遞順序
在圖中,軸心點(diǎn)(pivot point)是Apache與提供程序功能相當(dāng)?shù)牟糠?,通過(guò)它來(lái)和目標(biāo)的Web服務(wù)進(jìn)行交互,,它通常稱為Provider。axis中常用的Provider有Java:RPC,java:MSG,,java:EJB,。一個(gè)Web服務(wù)可以部署一個(gè)或者多個(gè)Handler。
Apache axis中的Handler體系結(jié)構(gòu)和JAX-RPC 1.0(JSR101)中的體系結(jié)構(gòu)稍有不同,,需要聲明的是,,本文的代碼在axis中開(kāi)發(fā),故需要在axis環(huán)境下運(yùn)行,。
在axis環(huán)境下,,SOAP消息Handler必須實(shí)現(xiàn)org.apache.axis.Handler接口(在JAX-RPC 1.0規(guī)范中,SOAP消息Handler必須實(shí)現(xiàn)javax.xml.rpc.handler.Handler接口),,org.apache.axis.Handler接口的部分代碼如下:
例程1 org.apache.axis.Handle的部分代碼
public interface Handler extends Serializable {
public void init();
public void cleanup();
public void invoke(MessageContext msgContext) throws AxisFault ;
public void onFault(MessageContext msgContext);
public void setOption(String name, Object value);
public Object getOption(String name);
public void setName(String name);
public String getName();
public Element getDeploymentData(Document doc);
public void generateWSDL(MessageContext msgContext) throws AxisFault;
…
}
為了提供開(kāi)發(fā)的方便,,在編寫Handler時(shí),只要繼承org.apache.axis.handlers. BasicHandler即可,,BasicHandler是Handler的一個(gè)模板,,我們看它的部分代碼:
例程2 BasicHandler的部分代碼
public abstract class BasicHandler implements Handler {
protected static Log log =
LogFactory.getLog(BasicHandler.class.getName());
protected Hashtable options;
protected String name;
//這個(gè)方法必須在Handler中實(shí)現(xiàn)。
public abstract void invoke(MessageContext msgContext) throws AxisFault;
public void setOption(String name, Object value) {
if ( options == null ) initHashtable();
options.put( name, value );
}
…
}
BasicHandler中的(MessageContext msgContext)方法是Handler實(shí)現(xiàn)類必須實(shí)現(xiàn)的方法,,它通過(guò)MessageContext來(lái)獲得請(qǐng)求或者響應(yīng)的SOAPMessage對(duì)象,,然后對(duì)SOAPMessage進(jìn)行處理。
在介紹Handler的開(kāi)發(fā)之前,,我們先來(lái)看一下目標(biāo)Web服務(wù)的端點(diǎn)實(shí)現(xiàn)類的代碼,,如例程3所示。
例程3 目標(biāo)Web服務(wù)的端點(diǎn)實(shí)現(xiàn)類
package com.hellking.webservice;
public class HandleredService
{
//一個(gè)簡(jiǎn)單的Web服務(wù)
public String publicMethod(String name)
{
return "Hello!"+name;
}
}
//另一個(gè)Web服務(wù)端點(diǎn):
package com.hellking.webservice;
public class OrderService
{
//web服務(wù)方法:獲得客戶端的訂單信息,,并且對(duì)訂單信息進(jìn)行對(duì)應(yīng)的處理,,
通常情況是把訂單的信息寫入數(shù)據(jù)庫(kù),然后可客戶端返回確認(rèn)信息,。
public String orderProduct(String name,String address,String item,int quantity,Card card)
{
String cardId=card.getCardId();
String cardType=card.getCardType();
String password=card.getPassword();
String rderInfo="name="+name+",address="+address+",item="+item+",quantity="+quantity+"
,cardId="+cardId+",cardType="+cardType+",password="+password;
System.out.println("這里是客戶端發(fā)送來(lái)的信息:");
System.out.println(orderInfo);
return orderInfo;
}
}
下面我們分不同情況討論Handler的使用實(shí)例。
使用Handler為系統(tǒng)做日志
Handler為系統(tǒng)做日志是一種比較常見(jiàn)而且簡(jiǎn)單的使用方式,。和Servlet中的Filter一樣,,我們可以使用Handler來(lái)把用戶的訪問(wèn)寫入系統(tǒng)日志。下面我們來(lái)看日志Handler的具體代碼,,如例程4所示,。
例程4 LogHandler的代碼
package com.hellking.webservice;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.util.Date;
import org.apache.axis.AxisFault;
import org.apache.axis.Handler;
import org.apache.axis.MessageContext;
import org.apache.axis.handlers.BasicHandler;
public class LogHandler extends BasicHandler {
/**invoke,每一個(gè)handler都必須實(shí)現(xiàn)的方法,。
*/
public void invoke(MessageContext msgContext) throws AxisFault
{
//每當(dāng)web服務(wù)被調(diào)用,,都記錄到log中。
try {
Handler handler = msgContext.getService();
String filename = (String)getOption("filename");
if ((filename == null) || (filename.equals("")))
throw new AxisFault("Server.NoLogFile",
"No log file configured for the LogHandler!",
null, null);
FileOutputStream fos = new FileOutputStream(filename, true);
PrintWriter writer = new PrintWriter(fos);
Integer counter = (Integer)handler.getOption("accesses");
if (counter == null)
counter = new Integer(0);
counter = new Integer(counter.intValue() + 1);
Date date = new Date();
msgContext.getMessage().writeTo(System.out);
String result = "在"+date + ": Web 服務(wù) " +
msgContext.getTargetService() +
" 被調(diào)用,,現(xiàn)在已經(jīng)共調(diào)用了 " + counter + " 次.";
handler.setOption("accesses", counter);
writer.println(result);
writer.close();
} catch (Exception e) {
throw AxisFault.makeFault(e);
}
}
}
前面我們說(shuō)過(guò),,Handler實(shí)現(xiàn)類必須實(shí)現(xiàn)invoke方法,invoke方法是Handler處理其業(yè)務(wù)的入口點(diǎn)。LogHandler的主要功能是把客戶端訪問(wèn)的Web服務(wù)的名稱和訪問(wèn)時(shí)間,、訪問(wèn)的次數(shù)記錄到一個(gè)日志文件中,。
下面部署這個(gè)前面開(kāi)發(fā)的Web服務(wù)對(duì)像,然后為Web服務(wù)指定Handler,。編輯Axis_Home/WEB-INF/ server-config.wsdd文件,,在其中加入以下的內(nèi)容:
<service name="HandleredService" provider="java:RPC">
<parameter name="allowedMethods" value="*"/>
<parameter name="className" value="com.hellking.webservice.HandleredService"/>
<parameter name="allowedRoles" value="chen"/>
<beanMapping languageSpecificType="java:com.hellking.webservice.Card"
qname="card:card" xmlns:card="card"/>
<requestFlow>
<handler name="logging" type="java:com.hellking.webservice.LogHandler">
<parameter name="filename" value="c:\\MyService.log"/>
</handler>
</requestFlow>
</service>
…
</globalConfiguration>
…
<handler name="logging" type="java:com.hellking.webservice.LogHandler">
<parameter name="filename" value="c:\\MyService.log"/>
</handler>
…
<service name="HandleredService" provider="java:RPC">
…
<requestFlow>
<handler type="logging"/>
…<!--在這里可以指定多個(gè)Handler-->
</requestFlow>
</service>
http://127.0.0.1:8080/handler/services/HandleredService?wsdl&method=publicMethod&name=chen
注意:這個(gè)URL需要根據(jù)具體情況改變。
在Sun Jul 06 22:42:03 CST 2003: Web 服務(wù) HandleredService 被調(diào)用,,現(xiàn)在已經(jīng)共調(diào)用了 1 次.
在Sun Jul 06 22:42:06 CST 2003: Web 服務(wù) HandleredService 被調(diào)用,,現(xiàn)在已經(jīng)共調(diào)用了 2 次.
在Sun Jul 06 22:42:13 CST 2003: Web 服務(wù) HandleredService 被調(diào)用,現(xiàn)在已經(jīng)共調(diào)用了 3 次.
使用Handler對(duì)用戶的訪問(wèn)認(rèn)證
使用Handler為用戶訪問(wèn)認(rèn)證也是它的典型使用,,通過(guò)它,,可以減少在Web服務(wù)端代碼中認(rèn)證的麻煩,同時(shí)可以在部署描述符中靈活改變用戶的訪問(wèn)權(quán)限,。
對(duì)用戶認(rèn)證的Handler代碼如下:
例程5 認(rèn)證的Handler
package com.hellking.webservice;
import….
//此handler的目的是對(duì)用戶認(rèn)證,,只有認(rèn)證的用戶才能訪問(wèn)目標(biāo)服務(wù)。
public class AuthenticationHandler extends BasicHandler
{
/**invoke,,每一個(gè)handler都必須實(shí)現(xiàn)的方法,。
*/
public void invoke(MessageContext msgContext)throws AxisFault
{
SecurityProvider provider = (SecurityProvider)msgContext.getProperty("securityProvider");
if(provider==null)
{
provider= new SimpleSecurityProvider();
msgContext.setProperty("securityProvider", provider);
}
if(provider!=null)
{
String userId=msgContext.getUsername();
String password=msgContext.getPassword();
//對(duì)用戶進(jìn)行認(rèn)證,如果authUser==null,,表示沒(méi)有通過(guò)認(rèn)證,,
拋出Server.Unauthenticated異常。
org.apache.axis.security.AuthenticatedUser authUser
= provider.authenticate(msgContext);
if(authUser==null)
throw new AxisFault("Server.Unauthenticated",
Messages.getMessage("cantAuth01", userId), null,null);
//用戶通過(guò)認(rèn)證,,把用戶的設(shè)置成認(rèn)證了的用戶,。
msgContext.setProperty("authenticatedUser", authUser);
}
}
}
在AuthenticationHandler代碼里,它從MessageContext中獲得用戶信息,,然后進(jìn)行認(rèn)證,,如果認(rèn)證成功,那么就使用msgContext.setProperty("authenticatedUser", authUser)方法把用戶設(shè)置成認(rèn)證了的用戶,,如果認(rèn)證不成功,,那么就拋出Server.Unauthenticated異常,。
部署這個(gè)Handler,,同樣,,在server-config里加入以下的內(nèi)容:
<handler name="authen" type="java:com.hellking.webservice.AuthenticationHandler"/>
…
<service name="HandleredService" provider="java:RPC">
<parameter name="allowedRoles" value="chen"/>
…
</service>
WEB-INF/users.lst文件中加入以下用戶:
hellking hellking
chen chen
http://127.0.0.1:8080/handler/services/HandleredService?wsdl&method=publicMethod&name=chen
將會(huì)提示輸入用戶名和密碼,如圖2所示,。
圖2 訪問(wèn)web服務(wù)時(shí)的驗(yàn)證
如果客戶端是應(yīng)用程序,,那么可以這樣在客戶端設(shè)置用戶名和密碼:
例程6 在客戶端設(shè)置用戶名和密碼
http://127.0.0.1:808
String endpointURL = "http://127.0.0.1:8080/handler/services/HandleredService?wsdl";
Service service = new Service();
Call call = (Call) service.createCall();
call.setTargetEndpointAddress( new java.net.URL(endpointURL) );
call.setOperationName( new
QName("HandleredService", "orderProduct") );//設(shè)置操作的名稱。
//由于需要認(rèn)證,,故需要設(shè)置調(diào)用的用戶名和密碼,。
call.getMessageContext().setUsername("chen");
call.getMessageContext().setPassword("chen");
使用Handler對(duì)用戶的訪問(wèn)授權(quán)
對(duì)于已經(jīng)認(rèn)證了的用戶,,有時(shí)在他們操作某個(gè)特定的服務(wù)時(shí),還需要進(jìn)行授權(quán),,只有授權(quán)的用戶才能繼續(xù)進(jìn)行操作,。我們看對(duì)用戶進(jìn)行授權(quán)的Handler的代碼。
例程7 對(duì)用戶進(jìn)行授權(quán)的代碼
package com.hellking.webservice;
import…
//此handler的目的是對(duì)認(rèn)證的用戶授權(quán),,只有授權(quán)的用戶才能訪問(wèn)目標(biāo)服務(wù),。
public class AuthorizationHandler extends BasicHandler
{
/**invoke,每一個(gè)handler都必須實(shí)現(xiàn)的方法,。
*/
public void invoke(MessageContext msgContext)
throws AxisFault
{
AuthenticatedUser user = (AuthenticatedUser)msgContext.getProperty("authenticatedUser");
if(user == null)
throw new AxisFault("Server.NoUser", Messages.getMessage("needUser00"), null, null);
String userId = user.getName();
Handler serviceHandler = msgContext.getService();
if(serviceHandler == null)
throw new AxisFault(Messages.getMessage("needService00"));
String serviceName = serviceHandler.getName();
String allowedRoles = (String)serviceHandler.getOption("allowedRoles");
if(allowedRoles == null)
{
return;
}
SecurityProvider provider = (SecurityProvider)msgContext.getProperty("securityProvider");
if(provider == null)
throw new AxisFault(Messages.getMessage("noSecurity00"));
for(StringTokenizer st = new StringTokenizer(allowedRoles, ","); st.hasMoreTokens();)
{
String thisRole = st.nextToken();
if(provider.userMatches(user, thisRole))
{
return;//訪問(wèn)授權(quán)通過(guò),。
}
}
//沒(méi)有通過(guò)授權(quán),不能訪問(wèn)目標(biāo)服務(wù),,拋出Server.Unauthorized異常,。
throw new AxisFault("Server.Unauthorized",
Messages.getMessage("cantAuth02", userId, serviceName), null, null);
}
}
在service-config.wsdd文件中,我們?yōu)閃eb服務(wù)指定了以下的用戶:
<parameter name="allowedRoles" value="chen,hellking"/>
provider.userMatches(user, thisRole)將匹配允許訪問(wèn)Web服務(wù)的用戶,,如果匹配成功,,那么授權(quán)通過(guò),如果沒(méi)有授權(quán)成功,,那么拋出Server.Unauthorized異常,。
使用Handler對(duì)SOAP消息進(jìn)行加密、解密
由于SOAP消息在HTTP協(xié)議中傳輸,,而HTTP協(xié)議的安全度是比較低的,,怎么保證信息安全到達(dá)對(duì)方而不泄漏或中途被撰改,將是Web服務(wù)必須解決的問(wèn)題,。圍繞Web服務(wù)的安全,,有很多相關(guān)的技術(shù),比如WS-Security,,WS-Trace等,,另外,還有以下相關(guān)技術(shù):
XML Digital Signature(XML數(shù)字簽名)
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
不管使用什么技術(shù),,要使信息安全到達(dá)對(duì)方,,必須把它進(jìn)行加密,然后在對(duì)方收到信息后解密,。為了提供開(kāi)發(fā)的方便,可以使用Handler技術(shù),,在客戶端發(fā)送信息前,,使用客戶端的Handler對(duì)SOAP消息中的關(guān)鍵信息進(jìn)行加密;在服務(wù)端接收到消息后,,有相應(yīng)的Handler把消息進(jìn)行解密,,然后才把SOAP消息派發(fā)到目標(biāo)服務(wù),。
下面我們來(lái)看一個(gè)具體的例子。加入使用SOAP消息發(fā)送訂單的信息,,訂單的信息如下:
例程8 要發(fā)送的訂單SOAP消息
<soap-env:Envelope xmlns:soap-env="" target="_blank">http://schemas./soap/envelope/">
<soap-env:Header/>
<soapenv:Body>
<ns1:orderProduct soapenv:encodingStyle="http://schemas./soap/encod
ing/" xmlns:ns1="HandleredService">
<arg0 xsi:type="xsd:string">hellking</arg0>
<arg1 xsi:type="xsd:string">beijing</arg1>
<arg2 xsi:type="xsd:string">music-100</arg2>
<arg3 xsi:type="xsd:int">10</arg3>
<arg4 href="#id0"/>
</ns1:orderProduct>
<multiRef id="id0" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmls
oap.org/soap/encoding/" xsi:type="ns2:card" xmlns:soapenc="http://schemas.xmlsoa
p.org/soap/encoding/" xmlns:ns2="card">
<cardId xsi:type="xsd:string">234230572</cardId>
<cardType xsi:type="xsd:string">visa</cardType>
<password xsi:type="xsd:string">234kdsjf</password>
</multiRef>
</soapenv:Body>
</soap-env:Envelope>
上面的黑體字是傳輸?shù)拿舾行畔?,故需要加密。我們可以使用Message Digest之類的方法進(jìn)行加密,。加密之后的信息結(jié)構(gòu)如下:
例程9 把SOAP消息某些部分加密
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope …
<soapenv:Body>
<ns1:orderProduct …>
…
<arg4 href="#id0"/>
</ns1:orderProduct>
<multiRef …>
<ns3:EncryptedData xmlns:ns3="" target="_blank">http://www./2000/11/temp-xmlenc">
<ns3:DigestMethod Algorithm="http://www./2000/09/xmldsig#sha1"/>
<ns3:DigestValue>rO0ABXQAkyA8Y2FyZ…….
</ns3:DigestValue>
</ns3:EncryptedData>
</multiRef>
</soapenv:Body>
</soapenv:Envelope>
圖3是使用Handler對(duì)SOAP消息進(jìn)行加密,、解密后,SOAP消息在傳遞過(guò)程中結(jié)構(gòu)的改變,。
圖3 SOAP消息的加密和解密
從上圖可以看出,,通過(guò)使用加密、解密的Handler,,可以確保消息的安全傳遞,。進(jìn)一步說(shuō),如果把這種Handler做成通用的組件,,那么就可以靈活地部署到不同的服務(wù)端和客戶端,。
客戶端的Handler的功能是把SOAP消息使用一定的規(guī)則加密,假如使用Message Digest加密方式,,那么可以這樣對(duì)敏感的信息加密:
例程10 對(duì)SOAP消息的敏感部分加密
SOAPElement ele= soapBodyElement.addChildElement(envelope.createName
("EncryptedData","","http://www./2000/11/temp-xmlenc"));
ele.addChildElement("DigestMethod").addAttribute(envelope.createName
("Algorithm"),"http://www./2000/09/xmldsig#sha1");
byte[] digest=new byte[100];
ByteArrayOutputStream out=new ByteArrayOutputStream (100);
MessageDigest md = MessageDigest.getInstance("SHA");
ObjectOutputStream oos = new ObjectOutputStream(out);
//要加密的信息
String data = " <cardId xsi:type='xsd:string'>234230572
</cardId><cardType xsi:type='xsd:string'>visa</cardType>
<password xsi:type='xsd:string'>234kdsjf</password>";
byte buf[] = data.getBytes();
md.update(buf);
oos.writeObject(data);
oos.writeObject(md.digest());
digest=out.toByteArray();
out.close();
ele.addChildElement("DigestValue").addTextNode(new
sun.misc.BASE64Encoder().encode(digest));//對(duì)加密的信息編碼
在客戶端發(fā)送出SOAP消息時(shí),,客戶端的Handler攔截發(fā)送的SOAP消息,然后對(duì)它們進(jìn)行加密,,最后把加密的信息傳送到服務(wù)端,。
服務(wù)端接收到加密的信息后,解密的Handler會(huì)把對(duì)應(yīng)的加密信息解密,。服務(wù)端Handler代碼如例程11所示,。
例程11 服務(wù)端解密Handler
package com.hellking.webservice;
import…
//此handler的目的是把加密的SOAP消息解密成目標(biāo)服務(wù)可以使用的SOAP消息。
public class MessageDigestHandler extends BasicHandler
{
/**invoke,,每一個(gè)handler都必須實(shí)現(xiàn)的方法,。
*/
public void invoke(MessageContext msgContext)throws AxisFault
{
try
{
//從messageContext例取得SOAPMessage對(duì)象。
SOAPMessage msg=msgContext.getMessage();
SOAPEnvelope env=msg.getSOAPPart().getEnvelope();
Iterator it=env.getBody().getChildElements();
SOAPElement multi=null;
while(it.hasNext())
{
multi=(SOAPElement)it.next();//multi是soapbody的最后一個(gè)child,。
}
String value="";//value表示加密后的值,。
SOAPElement digestValue=null;
Iterator it2=multi.getChildElements();
while(it2.hasNext())
{
SOAPElement temp=(SOAPElement)it2.next();
Iterator it3=temp.getChildElements(env.createName("DigestValue",
"ns3","http://www./2000/11/temp-xmlenc"));
if(it3.hasNext())
value=((SOAPElement)it3.next()).getValue();//獲得加密的值
}
//把加密的SOAPMessage解密成目標(biāo)服務(wù)可以調(diào)用的SOAP消息。
SOAPMessage msg2=convertMessage(msg,this.decrypte(value));
msgContext.setMessage(msg2);
}
catch(Exception e)
{
e.printStackTrace();
}
}
//這個(gè)方法是把加密的數(shù)據(jù)進(jìn)行解密,,返回明文,。
public String decrypte(String value)
{
String data=null;
try
{
ByteArrayInputStream fis = new
ByteArrayInputStream(new sun.misc.BASE64Decoder().decodeBuffer(value));
ObjectInputStream ois = new ObjectInputStream(fis);
Object o = ois.readObject();
if (!(o instanceof String)) {
System.out.println("Unexpected data in string");
System.exit(-1);
}
data = (String) o;
System.out.println("解密后的值:" + data);
o = ois.readObject();
if (!(o instanceof byte[])) {
System.out.println("Unexpected data in string");
System.exit(-1);
}
byte origDigest[] = (byte []) o;
MessageDigest md = MessageDigest.getInstance("SHA");
md.update(data.getBytes());
}
…
return data;
}
//把解密后的信息重新組裝成服務(wù)端能夠使用的SOAP消息。
public SOAPMessage convertMessage(SOAPMessage msg,String data)
{
….
}
}
可以看出,,服務(wù)端解密的Handler和客戶端加密的Handler的操作是相反的過(guò)程,。
總結(jié)
通過(guò)以上的討論,相信大家已經(jīng)掌握了Handler的基本使用技巧,??梢钥闯?,通過(guò)使用Handler,可以給Web服務(wù)提供一些額外的功能,。在實(shí)際的開(kāi)發(fā)中,,我們可以開(kāi)發(fā)出一些通用的Handler,然后通過(guò)不同的搭配方式把它們部署到不同的Web服務(wù)中,。
+++++++++++++++++++++++++++++++++++++++
AXIS學(xué)習(xí)筆記(三)(建立安全的AXIS服務(wù)上)
ronghao100 原創(chuàng)
在前面的文章中,,我們實(shí)現(xiàn)了最簡(jiǎn)單的AXIS服務(wù)。現(xiàn)在我們一起來(lái)討論一下Web服務(wù)的安全問(wèn)題,。
根據(jù)應(yīng)用的對(duì)安全要求的級(jí)別不同,,可以采用不同的方式來(lái)實(shí)現(xiàn)安全性,以下是目前最常用的一些實(shí)現(xiàn)方式(從低到高排列):
1,、J2EE Web應(yīng)用默認(rèn)的訪問(wèn)控制(數(shù)據(jù)是明文的),;
2、使用axis的Handler進(jìn)行訪問(wèn)控制(數(shù)據(jù)是明文的),;
3,、使用Servlet過(guò)濾器(Filter)進(jìn)行訪問(wèn)控制(數(shù)據(jù)是明文的);
4,、使用SSL/HTTPS協(xié)議來(lái)傳輸(加密的數(shù)據(jù)傳輸協(xié)議),;
5、使用WS-Security規(guī)范對(duì)信息進(jìn)行加密與身份認(rèn)證(數(shù)據(jù)被加密傳輸),。
我們僅討論第2,、4、5種實(shí)現(xiàn)方式,。在此之前我們先來(lái)了解一下AXIS自帶的一個(gè)工具SOAPMonitor,。
一、SOAPMonitor的使用
打開(kāi)http://localhost:8080/axis/進(jìn)入AXIS的主頁(yè)面,,你會(huì)看見(jiàn):
SOAPMonitor-[disabled by default for security reasons] ,,默認(rèn)狀態(tài)下其是不可用的,現(xiàn)在我們就來(lái)激活它,。
1,、到目錄%TOMCAT_HOME%\webapps\axis下,你會(huì)找到SOAPMonitorApplet.java,,在命令行中編譯它:
javac -classpath %AXIS_HOME%\lib\axis.jar SOAPMonitorApplet.java
編譯完之后你會(huì)看見(jiàn)目錄下多了很多CLASS文件,,它們的名字是SOAPMonitorApplet*.class
2、在目錄%TOMCAT_HOME%\webapps\axis\WEB-INF下打開(kāi)server-config.wsdd文件,,將下面的兩部分代碼直
接加入其中相應(yīng)的位置
第一部分:
<handler name="soapmonitor" type="java:org.apache.axis.handlers.SOAPMonitorHandler">
<parameter name="wsdlURL" value="/axis/SOAPMonitorService-impl.wsdl"/>
<parameter name="namespace" value="" target="_blank">http:///wsdl/2001/12/SOAPMonitorService-impl.wsdl"/>
<parameter name="serviceName" value="SOAPMonitorService"/>
<parameter name="portName" value="Demo"/>
</handler>
第二部分:
<service name="SOAPMonitorService" provider="java:RPC">
<parameter name="allowedMethods" value="publishMessage"/>
<parameter name="className" value="org.apache.axis.monitor.SOAPMonitorService"/>
<parameter name="scope" value="Application"/>
</service>
3,、選擇你要監(jiān)控的服務(wù)
以上次的HelloWorld服務(wù)為例,在server-config.wsdd中你會(huì)找到這段代碼
<service name="HelloWorld" provider="java:RPC">
<parameter name="allowedMethods" value="sayHello"/>
<parameter name="className" value="HelloWorld"/>
</service>
在這段代碼中加入以下的代碼:
<requestFlow>
<handler type="soapmonitor"/>
</requestFlow>
<responseFlow>
<handler type="soapmonitor"/>
</responseFlow>
最后的樣子是:
<service name="HelloWorld" provider="java:RPC">
<requestFlow>
<handler type="soapmonitor"/>
</requestFlow>
<responseFlow>
<handler type="soapmonitor"/>
</responseFlow>
<parameter name="allowedMethods" value="sayHello"/>
<parameter name="className" value="HelloWorld"/>
</service>
這樣HelloWorld服務(wù)就被監(jiān)控了
4,、啟動(dòng)Tomcat,打開(kāi)http://localhost:8080/axis/SOAPMonitor,,你就會(huì)看到Applet界面,在
jbuilder2005中運(yùn)行我們上次寫的客戶端程序 TestClient.java,。OK,!你會(huì)在Applet界面看
見(jiàn)客戶端與服務(wù)器端互發(fā)的XML內(nèi)容,注意這里是明文,!
二,、使用axis的Handler進(jìn)行訪問(wèn)控制(對(duì)安全要求不高時(shí)推薦)
axis為Web服務(wù)的訪問(wèn)控制提供了相關(guān)的配置描述符,并且提供了一個(gè)訪問(wèn)控制的簡(jiǎn)單 Handler,。默認(rèn)情況下,,你只要在配置描述符中添加用戶,然后在Web服務(wù)器的部署描述符中自動(dòng)允許的角色即可,。
1,、在axis的配置文件users.lst(位于WEB-INF目錄下)中添加一個(gè)用戶,如"ronghao1111",,表示
用戶名為ronghao,,密碼為1111。
2,、把例HelloWorld的Web服務(wù)重新部署(新加的部分已標(biāo)出)
<service name="HelloWorld" provider="java:RPC">
<requestFlow>
<handler type="soapmonitor"/>
<handler type="Authenticate"/> //新加的AXIS自帶的Handler
</requestFlow>
<responseFlow>
<handler type="soapmonitor"/>
</responseFlow>
<parameter name="allowedMethods" value="sayHello"/>
<parameter name="allowedRoles" value="ronghao"/> //注意,,這里是新加的部分!
<parameter name="className" value="HelloWorld"/>
</service>
在這個(gè)部署描述符中,,指定HelloWorld服務(wù)只能被ronghao訪問(wèn)
3,、修改客戶端程序 TestClient.java,增加訪問(wèn)用戶名,、密碼(新加的部分已標(biāo)出)
TestClient.java
import org.apache.axis.client.Call;
import org.apache.axis.client.Service;
import javax.xml.rpc.ParameterMode;
public class TestClient
{
public static void main(String [] args) throws Exception {
String endpoint = "http://localhost:" +"8080"+ "/axis/HelloWorld";
Service service = new Service();
Call call = (Call) service.createCall();
call.getMessageContext().setUsername("ronghao");// 用戶名,。
call.getMessageContext().setPassword("1111");// 密碼
call.setTargetEndpointAddress( new java.net.URL(endpoint) );
call.setOperationName( "sayHello" );
String res = (String) call.invoke( new Object[] {} );
System.out.println( res );
}
}
執(zhí)行TestClient,能夠順利訪問(wèn)Web服務(wù),;如果修改用戶名或者密碼,,那么就不能訪問(wèn) 。同樣,,
你在http://localhost:8080/axis/SOAPMonitor中看到的請(qǐng)求和響應(yīng)的XML是明文,!
三、使用SSL/HTTPS協(xié)議來(lái)傳輸
Web服務(wù)也可以使用SSL作為傳輸協(xié)議,。雖然JAX-RPC并沒(méi)有強(qiáng)制規(guī)定是否使用SSL協(xié)議,,但在tomcat
下使用HTTPS協(xié)議。
1,、使用JDK自帶的工具創(chuàng)建密匙庫(kù)和信任庫(kù),。
1)通過(guò)使用以下的命令來(lái)創(chuàng)建服務(wù)器端的密匙庫(kù):
keytool -genkey -alias Server -keystore server.keystore -keyalg RSA
輸入keystore密碼: changeit
您的名字與姓氏是什么?
[Unknown]: Server
您的組織單位名稱是什么,?
[Unknown]: ec
您的組織名稱是什么,?
[Unknown]: ec
您所在的城市或區(qū)域名稱是什么,?
[Unknown]: beijing
您所在的州或省份名稱是什么?
[Unknown]: beijing
該單位的兩字母國(guó)家代碼是什么
[Unknown]: CN
CN=Server, OU=ec, O=ec, L=beijing, ST=beijing, C=CN 正確嗎,?
[否]: y
輸入<Server>的主密碼
(如果和 keystore 密碼相同,,按回車):
以上命令執(zhí)行完成后,將獲得一個(gè)名為server.keystore的密匙庫(kù),。
2)生成客戶端的信任庫(kù),。首先輸出RSA證書(shū):
keytool -export -alias Server -file test_axis.cer -storepass changeit -keystore server.keystore
然后把RSA證書(shū)輸入到一個(gè)新的信任庫(kù)文件中。這個(gè)信任庫(kù)被客戶端使用,,被用來(lái)驗(yàn)證服務(wù)器端的身份,。
keytool -import -file test_axis.cer -storepass changeit -keystore client.truststore -alias serverkey -noprompt
以上命令執(zhí)行完成后,將獲得一個(gè)名為client.truststore的信任庫(kù),。
3)同理生成客戶端的密匙庫(kù)client.keystore和服務(wù)器端的信任庫(kù)server.truststore.方便起見(jiàn)給出.bat文件
gen-cer-store.bat內(nèi)容如下:
set SERVER_DN="CN=Server, OU=ec, O=ec, L=BEIJINGC, S=BEIJING, C=CN"
set CLIENT_DN="CN=Client, OU=ec, O=ec, L=BEIJING, S=BEIJING, C=CN"
set KS_PASS=-storepass changeit
set KEYINFO=-keyalg RSA
keytool -genkey -alias Server -dname %SERVER_DN% %KS_PASS% -keystore server.keystore %KEYINFO% -keypass changeit
keytool -export -alias Server -file test_axis.cer %KS_PASS% -keystore server.keystore
keytool -import -file test_axis.cer %KS_PASS% -keystore client.truststore -alias serverkey -noprompt
keytool -genkey -alias Client -dname %CLIENT_DN% %KS_PASS% -keystore client.keystore %KEYINFO% -keypass changeit
keytool -export -alias Client -file test_axis.cer %KS_PASS% -keystore client.keystore
keytool -import -file test_axis.cer %KS_PASS% -keystore server.truststore -alias clientkey -noprompt
好的,,現(xiàn)在我們就有了四個(gè)文件:server.keystore,server.truststore,,client.keystore,,client.truststore
2、更改Tomcat的配置文件(server.xml),,增加以下部署描述符:(其實(shí)里面有,,只是被注釋掉了)
<Connector port="8440"
maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
enableLookups="false" disableUploadTimeout="true"
acceptCount="100" scheme="https" secure="true"
clientAuth="true" keystoreFile="f:\server.keystore" keystorePass="changeit"
truststoreFile="f:\server.truststore" truststorePass="changeit"
sslProtocol="TLS" />
3、把HelloWorld重新部署一次,,在server-config.wsdd中修改如下部署代碼,。(還原了而已)
<service name="HelloWorld" provider="java:RPC">
<requestFlow>
<handler type="soapmonitor"/>
</requestFlow>
<responseFlow>
<handler type="soapmonitor"/>
</responseFlow>
<parameter name="allowedMethods" value="sayHello"/>
<parameter name="className" value="HelloWorld"/>
</service>
4、修改客戶端程序 TestClient.java(修改的部分已標(biāo)出)
public class TestClient
{
public static void main(String [] args) throws Exception {
String endpoint = "https://localhost:" +"8440"+ "/axis/HelloWorld";//注意區(qū)別在這里,!https,!
Service service = new Service();
Call call = (Call) service.createCall();
call.setTargetEndpointAddress( new java.net.URL(endpoint) );
call.setOperationName( "sayHello" );
String res = (String) call.invoke( new Object[] {} );
System.out.println( res );
}
}
5、最后使用命令來(lái)執(zhí)行客戶端程序
java -cp %AXISCLASSPATH%
-Djavax.net.ssl.keyStore=client.keystore
-Djavax.net.ssl.keyStorePassword=changeit
-Djavax.net.ssl.trustStore=client.truststore
TestClient
+++++++++++++++++++++++++++++++++++++++
AXIS學(xué)習(xí)筆記(四)(建立安全的AXIS服務(wù)下)
ronghao100 原創(chuàng)
四,、使用WS-Security規(guī)范對(duì)信息進(jìn)行加密與身份認(rèn)證
我們打算用Handler結(jié)合WSSecurity實(shí)現(xiàn)Web服務(wù)安全(Handler的有關(guān)內(nèi)容請(qǐng)參閱AXIS學(xué)習(xí)筆記(二))
設(shè)想流程:用WSClientRequestHandler.java位于客戶端對(duì)客戶端發(fā)出的XML文檔進(jìn)行加密
WSServerRequestHandler.java位于服務(wù)器端對(duì)客戶端發(fā)出的加密后的XML文檔進(jìn)行解密
WSServerResponseHandler.java位于服務(wù)器端對(duì)服務(wù)器端返回的XML文檔進(jìn)行加密
WSClientResponseHandler.java位于客戶端對(duì)服務(wù)器端返回的XML文檔進(jìn)行解密
1,、使用ISNetworks安全提供者,ISNetworks實(shí)現(xiàn)了RSA加密,、解密算法,。
當(dāng)然,你也可以使用其它的安全提供者,,并且可以使用不同的加密算法,。
ISNetworks相關(guān)包ISNetworksProvider.jar??截惖?TOMCAT_HOME% \webapps\axis\WEB-INF\lib
2,、Trust Services Integration Kit提供了一個(gè)WS-Security實(shí)現(xiàn)。你可以從http://www.獲得相關(guān)庫(kù)文件,分別是ws-security.jar和tsik.jar,。ws-security.jar中包含一個(gè)WSSecurity類,,我們使用它來(lái)對(duì)XML進(jìn)行數(shù)字簽名和驗(yàn)證,加密與解密,。同樣拷貝到%TOMCAT_HOME%\webapps\axis\WEB-INF\lib
3,、創(chuàng)建密匙庫(kù)和信任庫(kù)。(見(jiàn)上文,,一模一樣!)
4,、框架結(jié)構(gòu)
WSClientHandler.java //基類,,包含了一些公用方法
WSClientRequestHandler.java //繼承于WSClientHandler.java,調(diào)用WSHelper.java對(duì)客戶端發(fā)出的XML文檔進(jìn)行加密
WSClientResponseHandler.java //繼承于WSClientHandler.java,,調(diào)用WSHelper.java對(duì)服務(wù)器端返回的XML文檔進(jìn)行解密
WSServerHandler.java //基類,,包含了一些公用方法
WSServerRequestHandler.java //繼承于WSServerHandler.java,調(diào)用WSHelper.java對(duì)客戶端發(fā)出的加密后的XML文檔進(jìn)行解密
WSServerResponseHandler.java//繼承于WSServerHandler.java,,調(diào)用WSHelper.java對(duì)服務(wù)器端返回的XML文檔進(jìn)行加密
WSHelper.java //核心類,,對(duì)SOAP消息簽名、加密,、解密,、身份驗(yàn)證
MessageConverter.java //幫助類,Document,、SOAP消息互相轉(zhuǎn)換
5,、具體分析(在此強(qiáng)烈建議看一下tsik.jar的API)
WSHelper.java
public class WSHelper {
static String PROVIDER="ISNetworks";//JSSE安全提供者。
//添加JSSE安全提供者,,你也可以使用其它安全提供者,。只要支持DESede算法。這是程序里動(dòng)態(tài)加載還可以在JDK中靜態(tài)加載
static
{
java.security.Security.addProvider(new com.isnetworks.provider.jce.ISNetworksProvider());
}
/**
*對(duì)XML文檔進(jìn)行數(shù)字簽名,。
*/
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();//ws-security.jar中包含的WSSecurity類
wSSecurity.sign(doc, sk, ki);//簽名,。
}
/**
*對(duì)XML文檔進(jìn)行身份驗(yàn)證。
*/
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;
}
/**
*對(duì)XML文檔進(jìn)行加密,。必須有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();
}
}
/**
*對(duì)文檔進(jìn)行解密,。
*/
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相關(guān)的元素。
}
}
WSClientHandler.java
//繼承自org.apache.axis.handlers.BasicHandler即AXIS內(nèi)在的
public class WSClientHandler extends BasicHandler{
protected String keyStoreFile ;
protected String keyStoreType ="JKS";//默認(rèn)
protected String keyStorePassword ;
protected String keyAlias ;
protected String keyEntryPassword ;
protected String trustStoreFile ;
protected String trustStoreType = "JKS";//默認(rèn)
protected String trustStorePassword ;
protected String certAlias ;
public void setInitialization(String keyStoreFile,String keyStoreType,String keyStorePassword,
String keyAlias,String keyEntryPassword,String trustStoreFile,
String trustStoreType,String trustStorePassword,String certAlias){
this.keyStoreFile=keyStoreFile;
this.keyStoreType=keyStoreType;
this.keyStorePassword=keyStorePassword;
this.keyAlias=keyAlias;
this.keyEntryPassword=keyEntryPassword;
this.trustStoreFile=trustStoreFile;
this.trustStoreType=trustStoreType;
this.trustStorePassword=trustStorePassword;
this.certAlias=certAlias;
}
public void setInitialization(String keyStoreFile,String keyStorePassword,
String keyAlias,String keyEntryPassword,String trustStoreFile,
String trustStorePassword,String certAlias){
this.keyStoreFile=keyStoreFile;
this.keyStorePassword=keyStorePassword;
this.keyAlias=keyAlias;
this.keyEntryPassword=keyEntryPassword;
this.trustStoreFile=trustStoreFile;
this.trustStorePassword=trustStorePassword;
this.certAlias=certAlias;
}
public void invoke(MessageContext messageContext) throws AxisFault {//在這個(gè)方法里對(duì)XML文檔進(jìn)行處理
//do nothing now!
}
public void onFault(MessageContext msgContext) {
System.out.println("處理錯(cuò)誤,,這里忽略,!");
}
}
WSClientRequestHandler.java
public class WSClientRequestHandler extends WSClientHandler{
public void invoke(MessageContext messageContext) throws AxisFault {
try {
SOAPMessage soapMessage = messageContext.getMessage();
Document doc = MessageConverter.convertSoapMessageToDocument(soapMessage); //soapMessage轉(zhuǎn)換為Document
WSHelper.sign(doc, keyStoreFile, keyStoreType,keyStorePassword, keyAlias, keyEntryPassword); //數(shù)字簽名
WSHelper.encrypt(doc, trustStoreFile, trustStoreType, trustStorePassword, certAlias); //加密
soapMessage = MessageConverter.convertDocumentToSOAPMessage(doc);
//處理后的Document再轉(zhuǎn)換回soapMessage
messageContext.setMessage(soapMessage);
} catch (Exception e){
System.err.println("在處理響應(yīng)時(shí)發(fā)生以下錯(cuò)誤: " + e);
e.printStackTrace(); }
}
}
WSClientResponseHandler.java
public class WSClientResponseHandler extends WSClientHandler{
public void invoke(MessageContext messageContext) throws AxisFault {
try {
SOAPMessage soapMessage = messageContext.getCurrentMessage();
Document doc = MessageConverter.convertSoapMessageToDocument(soapMessage);
WSHelper.decrypt(doc, keyStoreFile, keyStoreType,
keyStorePassword, keyAlias, keyEntryPassword);//解密
WSHelper.verify(doc, trustStoreFile, trustStoreType, trustStorePassword);//驗(yàn)證
WSHelper.removeWSSElements(doc);
soapMessage = MessageConverter.convertDocumentToSOAPMessage(doc);
messageContext.setMessage(soapMessage);
} catch (Exception e){
e.printStackTrace();
System.err.println("在處理響應(yīng)時(shí)發(fā)生以下錯(cuò)誤: " + e);
}
}
}
WSServerHandler.java
public class WSServerHandler extends BasicHandler{
protected String keyStoreFile ;
protected String keyStoreType ="JKS";//默認(rèn)
protected String keyStorePassword ;
protected String keyAlias ;
protected String keyEntryPassword ;
protected String trustStoreFile ;
protected String trustStoreType = "JKS";//默認(rèn)
protected String trustStorePassword ;
protected String certAlias ;
public void invoke(MessageContext messageContext) throws AxisFault {
//do nothing now!
}
public void onFault(MessageContext msgContext) {
System.out.println("處理錯(cuò)誤,這里忽略!");
}
public void init() { //初始化,,從配置文件server-config.wsdd中讀取屬性
keyStoreFile = (String)getOption("keyStoreFile");
if(( keyStoreFile== null) )
System.err.println("Please keyStoreFile configured for the Handler!");
trustStoreFile = (String)getOption("trustStoreFile");
if(( trustStoreFile== null) )
System.err.println("Please trustStoreFile configured for the Handler!");
keyStorePassword = (String)getOption("keyStorePassword");
if(( keyStorePassword== null) )
System.err.println("Please keyStorePassword configured for the Handler!");
keyAlias = (String)getOption("keyAlias");
if(( keyAlias== null) )
System.err.println("Please keyAlias configured for the Handler!");
keyEntryPassword = (String)getOption("keyEntryPassword");
if(( keyEntryPassword== null) )
System.err.println("Please keyEntryPassword configured for the Handler!");
trustStorePassword = (String)getOption("trustStorePassword");
if(( trustStorePassword== null) )
System.err.println("Please trustStorePassword configured for the Handler!");
certAlias = (String)getOption("certAlias");
if ((certAlias==null))
System.err.println("Please certAlias configured for the Handler!");
if ((getOption("keyStoreType")) != null)
keyStoreType = (String)getOption("keyStoreType");
if ((getOption("trustStoreType")) != null)
trustStoreType = (String)getOption("trustStoreType");
}
}
WSServerRequestHandler.java
public class WSServerRequestHandler extends WSServerHandler{
public void invoke(MessageContext messageContext) throws AxisFault {
try {
SOAPMessage msg = messageContext.getCurrentMessage();
Document doc = MessageConverter.convertSoapMessageToDocument(msg);
System.out.println("接收的原始消息:");
msg.writeTo(System.out);
WSHelper.decrypt(doc, keyStoreFile, keyStoreType,
keyStorePassword, keyAlias, keyEntryPassword);//解密
WSHelper.verify(doc, trustStoreFile, trustStoreType, trustStorePassword);//驗(yàn)證
WSHelper.removeWSSElements(doc);
msg = MessageConverter.convertDocumentToSOAPMessage(doc);
System.out.println("懷原后的原始消息:");
msg.writeTo(System.out);
messageContext.setMessage(msg);
} catch (Exception e){
e.printStackTrace();
System.err.println("在處理響應(yīng)時(shí)發(fā)生以下錯(cuò)誤: " + e);
}
}
}
WSServerResponseHandler.java
public class WSServerResponseHandler extends WSServerHandler{
public void invoke(MessageContext messageContext) throws AxisFault {
try {
SOAPMessage soapMessage = messageContext.getMessage();
System.out.println("返回的原始消息:");
soapMessage.writeTo(System.out);
Document doc = MessageConverter.convertSoapMessageToDocument(soapMessage);
WSHelper.sign(doc, keyStoreFile, keyStoreType,
keyStorePassword, keyAlias, keyEntryPassword);//數(shù)字簽名
WSHelper.encrypt(doc, trustStoreFile, trustStoreType,//加密
trustStorePassword, certAlias);
soapMessage = MessageConverter.convertDocumentToSOAPMessage(doc);
System.out.println("返回的加密后的消息:");
soapMessage.writeTo(System.out);
messageContext.setMessage(soapMessage);
} catch (Exception e){
System.err.println("在處理響應(yīng)時(shí)發(fā)生以下錯(cuò)誤: " + e);
e.printStackTrace();
}
}
}
6,、應(yīng)用
為方便使用,把上述文件打包為ws-axis.jar,放入%TOMCAT_HOME%\webapps\axis\WEB-INF\lib
1)把HelloWorld重新部署一次,,在server-config.wsdd中修改如下部署代碼,。
<service name="HelloWorld" provider="java:RPC">
<parameter name="allowedMethods" value="*"/>
<parameter name="className" value="HelloWorld"/>
<requestFlow>
<handler type="soapmonitor"/>
<handler type="java:com.ronghao.WSAxis.WSServerRequestHandler">
<parameter name="keyStoreFile" value="f:\server.keystore"/>
<parameter name="trustStoreFile" value="f:\server.truststore"/>
<parameter name="keyStorePassword" value="changeit"/>
<parameter name="keyAlias" value="Server"/>
<parameter name="keyEntryPassword" value="changeit"/>
<parameter name="trustStorePassword" value="changeit"/>
<parameter name="certAlias" value="clientkey"/>
</handler>
</requestFlow>
<responseFlow>
<handler type="soapmonitor"/>
<handler type="java:com.ronghao.WSAxis.WSServerResponseHandler">
<parameter name="keyStoreFile" value="f:\server.keystore"/>
<parameter name="trustStoreFile" value="f:\server.truststore"/>
<parameter name="keyStorePassword" value="changeit"/>
<parameter name="keyAlias" value="Server"/>
<parameter name="keyEntryPassword" value="changeit"/>
<parameter name="trustStorePassword" value="changeit"/>
<parameter name="certAlias" value="clientkey"/>
</handler>
</responseFlow>
</service>
2)修改客戶端程序 TestClient.java(修改的部分已標(biāo)出,記著導(dǎo)入ws-axis.jar)
import javax.xml.namespace.QName;
import org.apache.axis.client.Call;
import org.apache.axis.client.Service;
import com.ronghao.WSAxis.*;
public class WSSClient1
{
public static void main(String [] args)
{
try {
//服務(wù)端的url,,需要根據(jù)情況更改,。
String endpointURL = "http://localhost:8080/axis/services/HelloWorld";
Service svc = new Service();
WSClientHandler handler=new WSClientRequestHandler();
//注意新加的HANDLER
handler.setInitialization("f:/client.keystore","changeit","Client","changeit",
"f:/client.truststore","changeit","serverkey");//初始化
WSClientHandler handlee=new WSClientResponseHandler();
//注意新加的HANDLER
handlee.setInitialization("f:/client.keystore","changeit","Client","changeit",
"f:/client.truststore","changeit","serverkey");//初始化
Call call =(Call)svc.createCall();
call.setClientHandlers(handler,handlee);//添加Handler
call.setTargetEndpointAddress(new java.net.URL(endpointURL));
call.setOperationName(new QName("sayHello"));
String result = (String) call.invoke( new Object [] {});
System.out.println("the result"+result);
} catch (Exception e) {
e.printStackTrace();
}
}
}
運(yùn)行的時(shí)候http://localhost:8080/axis/SOAPMonitor中看到的請(qǐng)求的XML就已加密!
總結(jié)
這里對(duì)代碼的解釋是不夠的,,很多概念沒(méi)有提到,。建議你最好看tsik.jar和AXIS的API深入了解。另外對(duì)ws-axis.jar的加解密實(shí)現(xiàn)打算運(yùn)用apache的wss4j,相關(guān)網(wǎng)址http://ws./ws-fx/wss4j/,。不過(guò)這個(gè)東西也應(yīng)該夠用了暫時(shí),。所有的源文件在附件中附件:soapTest.zip(279K)
++++++++++++++++++++++++++++++++++++++
AXIS學(xué)習(xí)筆記(五)( 在AXIS服務(wù)間傳遞JavaBean及其安全解決)
ronghao100 原創(chuàng)
在AXIS服務(wù)間傳遞JavaBean及其安全解決
這是AXIS學(xué)習(xí)筆記的最后一篇。在前面我們討論了最簡(jiǎn)單的HelloWorld服務(wù),,客戶端并沒(méi)有向服務(wù)器端
傳遞參數(shù),,現(xiàn)在我們來(lái)傳傳JavaBean。當(dāng)然,,也可以傳遞你自己定義的JAVA類,,但那樣你必須自己創(chuàng)建
專門的XML序列化器和反序列化器;而對(duì)JavaBean,,AXIS提供了現(xiàn)成的序列化器,。(有人說(shuō):懶惰是程序員最大的美德,我喜歡,,所以我就傳傳JavaBean)
一,、服務(wù)器端
1、CLASS類兩個(gè)Order.class,OrderTest.class,,位于%TOMCAT_HOME%\webapps\axis\WEB-INF\classes下
這兩個(gè)類都直接給出源碼,,不再說(shuō)明
Order.java
public class Order {
private String id;
private String name;
public void setId(String id){
this.id=id;
}
public String getId(){
return id;
}
public void setName(String name){
this.name=name;
}
public String getName(){
return name;
}
}
OrderTest.java
public class OrderTest {
public Order returnOrder(Order order){
Order newOrder=new Order();
if(order.getId().equals("1"))
newOrder.setName("ronghao");
else newOrder.setName("haorong");
return newOrder;
}
}
2、修改服務(wù)器端配置文件server-config.wsdd
在server-config.wsdd中相應(yīng)位置添加以下代碼
<service name="Order" provider="java:RPC">
<parameter name="allowedMethods" value="returnOrder"/>
<parameter name="className" value="OrderTest"/>
<beanMapping languageSpecificType="java:Order" qname="ns1:Order"
xmlns:ns1="urn:BeanService"/>
</service>
可以看到和前面的發(fā)布服務(wù)代碼相比僅多了一行代碼
<beanMapping languageSpecificType="java:Order" qname="ns1:Order"
xmlns:ns1="urn:BeanService"/>
languageSpecificType屬性指定JavaBean類文件位置,例如:
languageSpecificType="java:com.ronghao.axis.Order"
qname屬性指定JavaBean類的名字
其他是固定的,。
二,、客戶端
客戶端類文件一個(gè)OrderClient.class,代碼如下(變化的部分加注釋):
public class OrderClient
{
public static void main(String args[])
throws Exception
{
String endpoint = "http://localhost:8080/axis/services/Order"; //服務(wù)所在位置
Order order=new Order(); //JavaBean
order.setId("1");
Service service = new Service();
Call call = (Call)service.createCall();
//注冊(cè)JavaBean,,注意和server-config.wsdd中的配置代碼比較
QName qn = new QName("urn:BeanService", "Order");
call.registerTypeMapping(Order.class, qn, new BeanSerializerFactory(Order.class, qn),
new BeanDeserializerFactory(Order.class, qn));
String name="no!";
try
{
call.setTargetEndpointAddress(new URL(endpoint));
//調(diào)用的服務(wù)器端方法
call.setOperationName(new QName("Order", "returnOrder"));
//設(shè)定傳入的參數(shù),,這里qn即Order.class
call.addParameter("arg1", qn, ParameterMode.IN);
//設(shè)定返回的參數(shù)是Order.class
call.setReturnType(qn, Order.class);
Order result = (Order)call.invoke(new Object[] {
order
});
if(result != null)
name = result.getName();
}
catch(Exception e)
{
System.err.println(e);
}
System.out.println(name);
}
}
OK!運(yùn)行一下,,就可以看到返回了"ronghao",。
和上一篇文章一樣,,我們不容許在網(wǎng)絡(luò)中傳遞XML是明文,于是需要加密和驗(yàn)證,。這里我們繼續(xù)采用上次所講的框架,。(已打包成ws-axis.jar)
一、修改服務(wù)器端配置文件server-config.wsdd(和上一文章一模一樣,!不再羅嗦)
在server-config.wsdd中相應(yīng)位置添加以下代碼
<requestFlow>
<handler type="soapmonitor"/>
<handler type="java:com.ronghao.WSAxis.WSServerRequestHandler">
<parameter name="keyStoreFile" value="f:\server.keystore"/>
<parameter name="trustStoreFile" value="f:\server.truststore"/>
<parameter name="keyStorePassword" value="changeit"/>
<parameter name="keyAlias" value="Server"/>
<parameter name="keyEntryPassword" value="changeit"/>
<parameter name="trustStorePassword" value="changeit"/>
<parameter name="certAlias" value="clientkey"/>
</handler>
</requestFlow>
<responseFlow>
<handler type="soapmonitor"/>
<handler type="java:com.ronghao.WSAxis.WSServerResponseHandler">
<parameter name="keyStoreFile" value="f:\server.keystore"/>
<parameter name="trustStoreFile" value="f:\server.truststore"/>
<parameter name="keyStorePassword" value="changeit"/>
<parameter name="keyAlias" value="Server"/>
<parameter name="keyEntryPassword" value="changeit"/>
<parameter name="trustStorePassword" value="changeit"/>
<parameter name="certAlias" value="clientkey"/>
</handler>
</responseFlow>
二,、客戶端(區(qū)別就在這里,注意?。,。?br>首先在這里要說(shuō)一下,客戶端代碼編寫困擾了我很長(zhǎng)一段時(shí)間(整整一天),,因?yàn)樗⒉幌笪蚁胂蟮哪敲春?jiǎn)單,,當(dāng)然解決起來(lái)還是挺簡(jiǎn)單的:)問(wèn)題的解決經(jīng)歷了三個(gè)階段
第一階段:
在這個(gè)階段我想當(dāng)然的在OrderClient.class中加入了如下代碼:
WSClientHandler handler=new WSClientRequestHandler();//注意新加的HANDLER
handler.setInitialization("f:/client.keystore","changeit","Client","changeit",
"f:/client.truststore","changeit","serverkey");//初始化
WSClientHandler handlee=new WSClientResponseHandler();//注意新加的HANDLER
handlee.setInitialization("f:/client.keystore","changeit","Client","changeit",
"f:/client.truststore","changeit","serverkey");//初始化
call.setClientHandlers(handler,handlee);//添加Handler
這個(gè)方法也是我在上一文章里介紹的,結(jié)果拋出以下異常:
faultString: org.xml.sax.SAXException: Deserializing parameter
'newProfileReturn': could not find deserializer for type
{urn:BeanService Order}SerializableProfile
也就是說(shuō)不能正常解析XML文件,,于是理所當(dāng)然的郁悶了,覺(jué)得代碼中肯定漏設(shè)了CALL的一個(gè)屬性,,于是查看AXIS的源代碼,,沒(méi)有結(jié)果!轉(zhuǎn)機(jī)出現(xiàn)在下面一行代碼,,在不斷的拋出異常中我修改了代碼
將call.setClientHandlers(handler,handlee);改為
call.setClientHandlers(null,null);
結(jié)果程序還是拋出同樣的異常,,于是意識(shí)到這可能是AXIS的一個(gè)BUG,為證明這一點(diǎn),,我將下面的Handler初始化代碼刪除
WSClientHandler handler=new WSClientRequestHandler();//注意新加的HANDLER
handler.setInitialization("f:/client.keystore","changeit","Client","changeit",
"f:/client.truststore","changeit","serverkey");//初始化
WSClientHandler handlee=new WSClientResponseHandler();//注意新加的HANDLER
handlee.setInitialization("f:/client.keystore","changeit","Client","changeit",
"f:/client.truststore","changeit","serverkey");//初始化
結(jié)果還是拋出同樣的異常,,果然是BUG!得到這個(gè)結(jié)論后去了apache AXIS主頁(yè),,在問(wèn)題列表中見(jiàn)到了完全一樣問(wèn)題的提交,,但沒(méi)有解答(暈!)
最后得到了結(jié)論:call的setClientHandlers()方法只有當(dāng)call處理簡(jiǎn)單的數(shù)據(jù)類型,,如String,int等等才能正常使用,!
(當(dāng)然,如果你對(duì)這個(gè)問(wèn)題有不同的見(jiàn)解,,歡迎和我聯(lián)系,。或許我錯(cuò)了,,但程序不運(yùn)行是真的:))
第二階段:
開(kāi)始在google上找問(wèn)題的解決方法,,這也是我的習(xí)慣:)。找了一個(gè)類似問(wèn)題的討論,,地址如下:
http://marc./?l=axis-user&m=111259980822735&w=2
他們的解決方法是Handler繼承于javax.xml.rpc.handler.Handler,,然后在程序里動(dòng)態(tài)注冊(cè)而在我的ws-axis.jar里Handler繼承于org.apache.axis.handlers.BasicHandler。當(dāng)然,
javax.xml.rpc.handler.Handler是org.apache.axis.handlers.BasicHandler的老爸,,但在程序里老爸和兒子之間卻不能很好的兼容,,這也許就是所謂的代溝?,?無(wú)奈中重新寫了Handler,但在運(yùn)行中卻拋出異常,,提示message在被invoke的時(shí)候已被更改。我靠,,Handler的作用就是來(lái)更改message的?。∵@是什么世道,!
我知道很多程序采用的就是這種方法,,但我好象怎么修改都拋出上述異常。
第三階段
既然在程序里動(dòng)態(tài)注冊(cè)Handler行不通,,于是決定寫個(gè)單獨(dú)的配置文件來(lái)注冊(cè)Handler,。如果這種方法不幸失敗就返回第二階段。好馬為什么不吃回頭草,?,?
1、ws-axis.jar中修改WSClientHandler.class,修改后如下,,我想你一看就明白為何修改
public class WSClientHandler extends BasicHandler{
protected String keyStoreFile ;
protected String keyStoreType ="JKS";
protected String keyStorePassword ;
protected String keyAlias ;
protected String keyEntryPassword ;
protected String trustStoreFile ;
protected String trustStoreType = "JKS";
protected String trustStorePassword ;
protected String certAlias ;
public void init() {
keyStoreFile = (String)getOption("keyStoreFile");
if(( keyStoreFile== null) )
System.err.println("Please keyStoreFile configured for the Handler!");
trustStoreFile = (String)getOption("trustStoreFile");
if(( trustStoreFile== null) )
System.err.println("Please trustStoreFile configured for the Handler!");
keyStorePassword = (String)getOption("keyStorePassword");
if(( keyStorePassword== null) )
System.err.println("Please keyStorePassword configured for the Handler!");
keyAlias = (String)getOption("keyAlias");
if(( keyAlias== null) )
System.err.println("Please keyAlias configured for the Handler!");
keyEntryPassword = (String)getOption("keyEntryPassword");
if(( keyEntryPassword== null) )
System.err.println("Please keyEntryPassword configured for the Handler!");
trustStorePassword = (String)getOption("trustStorePassword");
if(( trustStorePassword== null) )
System.err.println("Please trustStorePassword configured for the Handler!");
certAlias = (String)getOption("certAlias");
if ((certAlias==null))
System.err.println("Please certAlias configured for the Handler!");
if ((getOption("keyStoreType")) != null)
keyStoreType = (String)getOption("keyStoreType");
if ((getOption("trustStoreType")) != null)
trustStoreType = (String)getOption("trustStoreType");
}
public void invoke(MessageContext messageContext) throws AxisFault {
//do nothing now!
}
public void onFault(MessageContext msgContext) {
System.out.println("處理錯(cuò)誤,,這里忽略!");
}
}
2,、寫客戶端的配置代碼client-config.wsdd,如下:
<?xml version="1.0" encoding="UTF-8"?>
<deployment name="defaultClientConfig"
xmlns="http://xml./axis/wsdd/"
xmlns:java="" target="_blank">http://xml./axis/wsdd/providers/java">
<transport name="http"
pivot="java:org.apache.axis.transport.http.HTTPSender"/>
<transport name="local"
pivot="java:org.apache.axis.transport.local.LocalSender"/>
<transport name="java"
pivot="java:org.apache.axis.transport.java.JavaSender"/>
<globalConfiguration>
<requestFlow>
<handler type="java:com.ronghao.WSAxis.WSClientRequestHandler">
<parameter name="keyStoreFile" value="D:\Tomcat5.5\webapps\axis\WEB-INF\client.keystore"/>
<parameter name="keyEntryPassword" value="changeit"/>
<parameter name="certAlias" value="serverkey"/>
<parameter name="trustStorePassword" value="changeit"/>
<parameter name="trustStoreFile" value="D:\Tomcat5.5\webapps\axis\WEB-INF\client.truststore"/>
<parameter name="keyAlias" value="Client"/>
<parameter name="keyStorePassword" value="changeit"/>
</handler>
</requestFlow>
<responseFlow>
<handler type="java:com.ronghao.WSAxis.WSClientResponseHandler">
<parameter name="keyStoreFile" value="D:\Tomcat5.5\webapps\axis\WEB-INF\client.keystore"/>
<parameter name="keyEntryPassword" value="changeit"/>
<parameter name="certAlias" value="serverkey"/>
<parameter name="trustStorePassword" value="changeit"/>
<parameter name="trustStoreFile" value="D:\Tomcat5.5\webapps\axis\WEB-INF\client.truststore"/>
<parameter name="keyAlias" value="Client"/>
<parameter name="keyStorePassword" value="changeit"/>
</handler>
</responseFlow>
</globalConfiguration>
</deployment>
同樣不再解釋,,不明白可以參考我的上一篇文章
3、修改OrderClient.class
在OrderClient.class中加入了如下代碼:
EngineConfiguration conf =
new FileProvider("F:\\Tomcat\\webapps\\axis\\WEB-INF\\client-config.wsdd");//位置
Service service = new Service(conf);
當(dāng)然記得導(dǎo)入
import org.apache.axis.EngineConfiguration;
import org.apache.axis.configuration.FileProvider;
運(yùn)行一下,,返回"ronghao",,靠,搞定,!
注意:這次我把OrderClient.class的調(diào)用放到了一個(gè)JSP文件中而不是jbuilder中,,因?yàn)橛衏lient-config.wsdd,所以你必須有完整的WEB程序發(fā)布到TOMCAT中,,否則會(huì)報(bào)找不到
相應(yīng)文件,。