目錄
1.發(fā)布web服務(wù)
2.發(fā)布Web服務(wù)使用Handler來增強Web服務(wù)的功能
3.建立安全的AXIS服務(wù)(上)
4.建立安全的AXIS服務(wù)(下)
5.在AXIS服務(wù)間傳遞JavaBean及其安全解決
AXIS學(xué)習(xí)筆記(一)
ronghao100 原創(chuàng)
前天頭告訴我用SOAP WEB服務(wù)開發(fā)一個客戶程序,用來與企業(yè)內(nèi)部的ERP進行交互。晚上趕快找相關(guān)的資料猛看,總算對SOAP有了一定的認識。干程序員這行真不容易,好象得不停地學(xué)習(xí)新東西,一不小心就被淘汰:(不過學(xué)習(xí)也是個很有意思的事情。好了,廢話少說,讓我們開始吧。
一、軟件環(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下需要加入三個包 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)入
三、實驗一下
在%AXIS_HOME%\webapps下找到axis文件夾,將其整個拷貝到%TOMCAT_HOME%\webapps下,啟動
Tomcat,打開瀏覽器訪問http://localhost:8080/axis/,出現(xiàn)以下頁面說明你配置成功了。很簡單吧:)
四、發(fā)布我們的第一個程序
第一個程序簡單的返回HELLO WORLD!
HelloWorld.java
public class HelloWorld {
public String sayHello()
{
return "HELLO WORLD!";
}
}
我們的第一種發(fā)布方式:
將HelloWorld.java拷貝到%TOMCAT_HOME%\webapps\axis下,然后將其改名為HelloWorld.jws,這樣AXIS就自然將其發(fā)布了。現(xià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)建一個Service實例,注意是必須的!
Call call = (Call) service.createCall();//創(chuàng)建Call實例,也是必須的!
call.setTargetEndpointAddress( new java.net.URL(endpoint) );//為Call設(shè)置服務(wù)的位置
call.setOperationName( "sayHello" );//注意方法名與HelloWorld.java中一樣??!
String res = (String) call.invoke( new Object[] {} );//返回String,沒有傳入?yún)?shù)
System.out.println( res );
}
}
我的測試是在jbuilder2005中,注意項目中要導(dǎo)入其自帶的AXIS包(當(dāng)然應(yīng)該把其中JAR文件替換一下),可以看到程序返回了 "HELLO WORLD!"
可以看到在AXIS里發(fā)布服務(wù)其實是一件很容易的事,這是因為這個服務(wù)很簡單的原因:)下面我們介紹第二種發(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="
<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
你會發(fā)現(xiàn)目錄下多了一個server-config.wsdd文件,這就是AXIS的配置文件,以后所有的服務(wù)發(fā)布描述都會在里面找到。(當(dāng)然,你可以直接修改它,不用再寫deploy.wsdd)然后打開瀏覽器http://localhost:8080/axis/servlet/AxisServlet,你就會看到你的服務(wù)已發(fā)布
注意:可以直接寫server-config.wsdd,如下:
同樣用客戶端程序訪問一下:(注意和上邊的差別!?。?/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 );
}
}
好了,相信你對AXIS已有了大致的了解。接下來將會涉及到傳參數(shù)、JAVABEAN對象,及AXIS的安全問題,下次再說吧:)也歡迎和我,一個快樂的JAVA程序員,聯(lián)系:)ronghao100@hotmail.com
第三種方式:
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.apache.org/axis/wsdd/" xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
<service provider="java:RPC">
<parameter value="server.SayHello.getName"/>
<parameter value="*"/>
</service>
</deployment>
3、發(fā)布服務(wù):
編輯一個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í)行這個批處理文件,這時候,如果提示成功的話,訪問http://localhost:8080/test/services 就會顯示服務(wù)列表。
4、生成客戶端client stub文件
在瀏覽器上訪問服務(wù)器端的服務(wù),可以下載到WSDL文件,通過Axis的相關(guān)工具,可以自動從WSDL文件中生成Web Service的客戶端代碼。
編寫一個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í)行這個批處理文件就可以生成client stub.
生成的stub client文件列表為:SayHello.java,SayHelloService.java,SayHelloServiceLocator.java,SayHelloSoapBindingStub.java .
5、編寫客戶端程序,編譯并執(zhí)行
下面是一段junit測試客戶端代碼。
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來增強Web服務(wù)的功能
Handler的基本概念
J2EE Web 服務(wù)中的Handler技術(shù)特點非常像Servlet技術(shù)中的Filter。我們知道,在Servlet中,當(dāng)一個HTTP到達服務(wù)端時,往往要經(jīng)過多個Filter對請求進行過濾,然后才到達提供服務(wù)的Servlet,這些Filter的功能往往是對請求進行統(tǒng)一編碼,對用戶進行認證,把用戶的訪問寫入系統(tǒng)日志等。相應(yīng)的,Web服務(wù)中的Handler通常也提供一下的功能:
對客戶端進行認證、授權(quán);
把用戶的訪問寫入系統(tǒng)日志;
對請求的SOAP消息進行加密,解密;
為Web Services對象做緩存。
SOAP消息Handler能夠訪問代表RPC請求或者響應(yīng)的SOAP消息。在JAX-RPC技術(shù)中,SOAP消息Handler可以部署在服務(wù)端,也可以在客戶端使用。
下面我們來看一個典型的SOAP消息Handler處理順序:
某個在線支付服務(wù)需要防止非授權(quán)的用戶訪問或者撰改服務(wù)端和客戶端傳輸?shù)男畔?,從而使用消息摘要(Message Digest)的方法對請求和響應(yīng)的SOAP消息進行加密。當(dāng)客戶端發(fā)送SOAP消⑹保?突Ф說?andler把請求消息中的某些敏感的信息(如信用卡密碼)進行加密,然后把加密后的SOAP消息傳輸?shù)椒?wù)端;服務(wù)端的SOAP消息Handler截取客戶端的請求,把請求的SOAP 消息進行解密,然后把解密后的SOAP消息派發(fā)到目標(biāo)的Web服務(wù)端點。
Apache axis是我們當(dāng)前開發(fā)Web服務(wù)的較好的選擇,使用axisWeb服務(wù)開發(fā)工具,可以使用Handler來對服務(wù)端的請求和響應(yīng)進行處理。典型的情況下,請求傳遞如圖1所示。
圖1 SOAP消息的傳遞順序
在圖中,軸心點(pivot point)是Apache與提供程序功能相當(dāng)?shù)牟糠?,通過它來和目標(biāo)的Web服務(wù)進行交互,它通常稱為Provider。axis中常用的Provider有Java:RPC,java:MSG,java:EJB。一個Web服務(wù)可以部署一個或者多個Handler。
Apache axis中的Handler體系結(jié)構(gòu)和JAX-RPC 1.0(JSR101)中的體系結(jié)構(gòu)稍有不同,需要聲明的是,本文的代碼在axis中開發(fā),故需要在axis環(huán)境下運行。
在axis環(huán)境下,SOAP消息Handler必須實現(xiàn)org.apache.axis.Handler接口(在JAX-RPC 1.0規(guī)范中,SOAP消息Handler必須實現(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;
…
}
為了提供開發(fā)的方便,在編寫Handler時,只要繼承org.apache.axis.handlers. BasicHandler即可,BasicHandler是Handler的一個模板,我們看它的部分代碼:
例程2 BasicHandler的部分代碼
public abstract class BasicHandler implements Handler {
protected static Log log =
LogFactory.getLog(BasicHandler.class.getName());
protected Hashtable options;
protected String name;
//這個方法必須在Handler中實現(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實現(xiàn)類必須實現(xiàn)的方法,它通過MessageContext來獲得請求或者響應(yīng)的SOAPMessage對象,然后對SOAPMessage進行處理。
在介紹Handler的開發(fā)之前,我們先來看一下目標(biāo)Web服務(wù)的端點實現(xiàn)類的代碼,如例程3所示。
例程3 目標(biāo)Web服務(wù)的端點實現(xiàn)類
package com.hellking.webservice;
public class HandleredService
{
//一個簡單的Web服務(wù)
public String publicMethod(String name)
{
return "Hello!"+name;
}
}
//另一個Web服務(wù)端點:
package com.hellking.webservice;
public class OrderService
{
//web服務(wù)方法:獲得客戶端的訂單信息,并且對訂單信息進行對應(yīng)的處理,
通常情況是把訂單的信息寫入數(shù)據(jù)庫,然后可客戶端返回確認信息。
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ā)送來的信息:");
System.out.println(orderInfo);
return orderInfo;
}
}
下面我們分不同情況討論Handler的使用實例。
使用Handler為系統(tǒng)做日志
Handler為系統(tǒng)做日志是一種比較常見而且簡單的使用方式。和Servlet中的Filter一樣,我們可以使用Handler來把用戶的訪問寫入系統(tǒng)日志。下面我們來看日志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,每一個handler都必須實現(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);
}
}
}
前面我們說過,Handler實現(xiàn)類必須實現(xiàn)invoke方法,invoke方法是Handler處理其業(yè)務(wù)的入口點。LogHandler的主要功能是把客戶端訪問的Web服務(wù)的名稱和訪問時間、訪問的次數(shù)記錄到一個日志文件中。
下面部署這個前面開發(fā)的Web服務(wù)對像,然后為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"/>
…<!--在這里可以指定多個Handler-->
</requestFlow>
</service>
http://127.0.0.1:8080/handler/services/HandleredService?wsdl&method=publicMethod&name=chen
注意:這個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對用戶的訪問認證
使用Handler為用戶訪問認證也是它的典型使用,通過它,可以減少在Web服務(wù)端代碼中認證的麻煩,同時可以在部署描述符中靈活改變用戶的訪問權(quán)限。
對用戶認證的Handler代碼如下:
例程5 認證的Handler
package com.hellking.webservice;
import….
//此handler的目的是對用戶認證,只有認證的用戶才能訪問目標(biāo)服務(wù)。
public class AuthenticationHandler extends BasicHandler
{
/**invoke,每一個handler都必須實現(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();
//對用戶進行認證,如果authUser==null,表示沒有通過認證,
拋出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);
//用戶通過認證,把用戶的設(shè)置成認證了的用戶。
msgContext.setProperty("authenticatedUser", authUser);
}
}
}
在AuthenticationHandler代碼里,它從MessageContext中獲得用戶信息,然后進行認證,如果認證成功,那么就使用msgContext.setProperty("authenticatedUser", authUser)方法把用戶設(shè)置成認證了的用戶,如果認證不成功,那么就拋出Server.Unauthenticated異常。
部署這個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
將會提示輸入用戶名和密碼,如圖2所示。
圖2 訪問web服務(wù)時的驗證
如果客戶端是應(yīng)用程序,那么可以這樣在客戶端設(shè)置用戶名和密碼:
例程6 在客戶端設(shè)置用戶名和密碼
使用Handler對用戶的訪問授權(quán) 對于已經(jīng)認證了的用戶,有時在他們操作某個特定的服務(wù)時,還需要進行授權(quán),只有授權(quán)的用戶才能繼續(xù)進行操作。我們看對用戶進行授權(quán)的Handler的代碼。 例程7 對用戶進行授權(quán)的代碼 import… //此handler的目的是對認證的用戶授權(quán),只有授權(quán)的用戶才能訪問目標(biāo)服務(wù)。 <parameter name="allowedRoles" value="chen,hellking"/> 使用Handler對SOAP消息進行加密、解密 由于SOAP消息在HTTP協(xié)議中傳輸,而HTTP協(xié)議的安全度是比較低的,怎么保證信息安全到達對方而不泄漏或中途被撰改,將是Web服務(wù)必須解決的問題。圍繞Web服務(wù)的安全,有很多相關(guān)的技術(shù),比如WS-Security,WS-Trace等,另外,還有以下相關(guān)技術(shù): XML Digital Signature(XML數(shù)字簽名) 下面我們來看一個具體的例子。加入使用SOAP消息發(fā)送訂單的信息,訂單的信息如下: 例程8 要發(fā)送的訂單SOAP消息 <soap-env:Envelope xmlns:soap-env=" 例程9 把SOAP消息某些部分加密 從上圖可以看出,通過使用加密、解密的Handler,可以確保消息的安全傳遞。進一步說,如果把這種Handler做成通用的組件,那么就可以靈活地部署到不同的服務(wù)端和客戶端。 客戶端的Handler的功能是把SOAP消息使用一定的規(guī)則加密,假如使用Message Digest加密方式,那么可以這樣對敏感的信息加密: 例程10 對SOAP消息的敏感部分加密 byte buf[] = data.getBytes(); 在客戶端發(fā)送出SOAP消息時,客戶端的Handler攔截發(fā)送的SOAP消息,然后對它們進行加密,最后把加密的信息傳送到服務(wù)端。 服務(wù)端接收到加密的信息后,解密的Handler會把對應(yīng)的加密信息解密。服務(wù)端Handler代碼如例程11所示。 例程11 服務(wù)端解密Handler 可以看出,服務(wù)端解密的Handler和客戶端加密的Handler的操作是相反的過程。 總結(jié) 通過以上的討論,相信大家已經(jīng)掌握了Handler的基本使用技巧。可以看出,通過使用Handler,可以給Web服務(wù)提供一些額外的功能。在實際的開發(fā)中,我們可以開發(fā)出一些通用的Handler,然后通過不同的搭配方式把它們部署到不同的Web服務(wù)中。 在前面的文章中,我們實現(xiàn)了最簡單的AXIS服務(wù)。現(xiàn)在我們一起來討論一下Web服務(wù)的安全問題。 2、在目錄%TOMCAT_HOME%\webapps\axis\WEB-INF下打開server-config.wsdd文件,將下面的兩部分代碼直 3、選擇你要監(jiān)控的服務(wù) 1、在axis的配置文件users.lst(位于WEB-INF目錄下)中添加一個用戶,如"ronghao1111",表示 import org.apache.axis.client.Call; public class TestClient Service service = new Service(); call.setOperationName( "sayHello" ); System.out.println( res ); 三、使用SSL/HTTPS協(xié)議來傳輸 1)通過使用以下的命令來創(chuàng)建服務(wù)器端的密匙庫: 輸入<Server>的主密碼 3)同理生成客戶端的密匙庫client.keystore和服務(wù)器端的信任庫server.truststore.方便起見給出.bat文件 keytool -genkey -alias Server -dname %SERVER_DN% %KS_PASS% -keystore server.keystore %KEYINFO% -keypass changeit keytool -genkey -alias Client -dname %CLIENT_DN% %KS_PASS% -keystore client.keystore %KEYINFO% -keypass changeit 2、更改Tomcat的配置文件(server.xml),增加以下部署描述符:(其實里面有,只是被注釋掉了) 3、把HelloWorld重新部署一次,在server-config.wsdd中修改如下部署代碼。(還原了而已) public class TestClient Service service = new Service(); call.setOperationName( "sayHello" ); System.out.println( res ); 5、最后使用命令來執(zhí)行客戶端程序 java -cp %AXISCLASSPATH% +++++++++++++++++++++++++++++++++++++++ 四、使用WS-Security規(guī)范對信息進行加密與身份認證 3、創(chuàng)建密匙庫和信任庫。(見上文,一模一樣?。?br> 5、具體分析(在此強烈建議看一下tsik.jar的API) WSSecurity wSSecurity = new WSSecurity(); wSSecurity.decrypt(doc, prvk2, null); public static void removeWSSElements(Document doc) throws Exception { } WSClientHandler.java public void setInitialization(String keyStoreFile,String keyStoreType,String keyStorePassword, WSClientRequestHandler.java SOAPMessage soapMessage = messageContext.getMessage(); WSClientResponseHandler.java SOAPMessage soapMessage = messageContext.getCurrentMessage(); WSHelper.decrypt(doc, keyStoreFile, keyStoreType, WSHelper.verify(doc, trustStoreFile, trustStoreType, trustStorePassword);//驗證 } public void invoke(MessageContext messageContext) throws AxisFault { WSHelper.verify(doc, trustStoreFile, trustStoreType, trustStorePassword);//驗證 } SOAPMessage soapMessage = messageContext.getMessage(); WSHelper.sign(doc, keyStoreFile, keyStoreType, soapMessage = MessageConverter.convertDocumentToSOAPMessage(doc); } 6、應(yīng)用 WSClientHandler handler=new WSClientRequestHandler(); String result = (String) call.invoke( new Object [] {}); } catch (Exception e) { 一、服務(wù)器端 public static void main(String args[]) 和上一篇文章一樣,我們不容許在網(wǎng)絡(luò)中傳遞XML是明文,于是需要加密和驗證。這里我們繼續(xù)采用上次所講的框架。(已打包成ws-axis.jar) 一、修改 第一階段: public class WSClientHandler extends BasicHandler{ public void init() { <globalConfiguration> 3、修改OrderClient.class
package com.hellking.webservice;
public class AuthorizationHandler extends BasicHandler
{
/**invoke,每一個handler都必須實現(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;//訪問授權(quán)通過。
}
}
//沒有通過授權(quá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ù)指定了以下的用戶:
provider.userMatches(user, thisRole)將匹配允許訪問Web服務(wù)的用戶,如果匹配成功,那么授權(quán)通過,如果沒有授權(quán)成功,那么拋出Server.Unauthorized異常。
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ù),要使信息安全到達對方,必須把它進行加密,然后在對方收到信息后解密。為了提供開發(fā)的方便,可以使用Handler技術(shù),在客戶端發(fā)送信息前,使用客戶端的Handler對SOAP消息中的關(guān)鍵信息進行加密;在服務(wù)端接收到消息后,有相應(yīng)的Handler把消息進行解密,然后才把SOAP消息派發(fā)到目標(biāo)服務(wù)。
<soap-env:Header/>
<soapenv:Body>
<ns1:orderProduct soapenv:encodingStyle=" </multiRef>
</soapenv:Body>
</soap-env:Envelope>
上面的黑體字是傳輸?shù)拿舾行畔ⅲ市枰用?。我們可以使用Message Digest之類的方法進行加密。加密之后的信息結(jié)構(gòu)如下:
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope …
<soapenv:Body>
<ns1:orderProduct …>
…
<arg4 href="#id0"/>
</ns1:orderProduct>
<multiRef …>
<ns3:EncryptedData xmlns:ns3="
<ns3:DigestMethod Algorithm=" <ns3:DigestValue>rO0ABXQAkyA8Y2FyZ…….
</ns3:DigestValue>
</ns3:EncryptedData>
</multiRef>
</soapenv:Body>
</soapenv:Envelope>
圖3是使用Handler對SOAP消息進行加密、解密后,SOAP消息在傳遞過程中結(jié)構(gòu)的改變。
圖3 SOAP消息的加密和解密
SOAPElement ele= soapBodyElement.addChildElement(envelope.createName
("EncryptedData","","
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));//對加密的信息編碼
package com.hellking.webservice;
import…
//此handler的目的是把加密的SOAP消息解密成目標(biāo)服務(wù)可以使用的SOAP消息。
public class MessageDigestHandler extends BasicHandler
{
/**invoke,每一個handler都必須實現(xiàn)的方法。
*/
public void invoke(MessageContext msgContext)throws AxisFault
{
try
{
//從messageContext例取得SOAPMessage對象。
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的最后一個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"," md.update(data.getBytes());
}
…
return data;
}
//把解密后的信息重新組裝成服務(wù)端能夠使用的SOAP消息。
public SOAPMessage convertMessage(SOAPMessage msg,String data)
{
….
}
}
+++++++++++++++++++++++++++++++++++++++
AXIS學(xué)習(xí)筆記(三)(建立安全的AXIS服務(wù)上)
ronghao100 原創(chuàng)
根據(jù)應(yīng)用的對安全要求的級別不同,可以采用不同的方式來實現(xiàn)安全性,以下是目前最常用的一些實現(xiàn)方式(從低到高排列):
1、J2EE Web應(yīng)用默認的訪問控制(數(shù)據(jù)是明文的);
2、使用axis的Handler進行訪問控制(數(shù)據(jù)是明文的);
3、使用Servlet過濾器(Filter)進行訪問控制(數(shù)據(jù)是明文的);
4、使用SSL/HTTPS協(xié)議來傳輸(加密的數(shù)據(jù)傳輸協(xié)議);
5、使用WS-Security規(guī)范對信息進行加密與身份認證(數(shù)據(jù)被加密傳輸)。
我們僅討論第2、4、5種實現(xiàn)方式。在此之前我們先來了解一下AXIS自帶的一個工具SOAPMonitor。
一、SOAPMonitor的使用
打開http://localhost:8080/axis/進入AXIS的主頁面,你會看見:
SOAPMonitor-[disabled by default for security reasons] ,默認狀態(tài)下其是不可用的,現(xiàn)在我們就來激活它。
1、到目錄%TOMCAT_HOME%\webapps\axis下,你會找到SOAPMonitorApplet.java,在命令行中編譯它:
javac -classpath %AXIS_HOME%\lib\axis.jar SOAPMonitorApplet.java
編譯完之后你會看見目錄下多了很多CLASS文件,它們的名字是SOAPMonitorApplet*.class
接加入其中相應(yīng)的位置
第一部分:
<handler name="soapmonitor" type="java:org.apache.axis.handlers.SOAPMonitorHandler">
<parameter name="wsdlURL" value="/axis/SOAPMonitorService-impl.wsdl"/>
<parameter name="namespace" value="
<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>
以上次的HelloWorld服務(wù)為例,在server-config.wsdd中你會找到這段代碼
<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、啟動Tomcat,打開http://localhost:8080/axis/SOAPMonitor,你就會看到Applet界面,在
jbuilder2005中運行我們上次寫的客戶端程序 TestClient.java。OK!你會在Applet界面看
見客戶端與服務(wù)器端互發(fā)的XML內(nèi)容,注意這里是明文!
二、使用axis的Handler進行訪問控制(對安全要求不高時推薦)
axis為Web服務(wù)的訪問控制提供了相關(guān)的配置描述符,并且提供了一個訪問控制的簡單 Handler。默認情況下,你只要在配置描述符中添加用戶,然后在Web服務(wù)器的部署描述符中自動允許的角色即可。
用戶名為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>
在這個部署描述符中,指定HelloWorld服務(wù)只能被ronghao訪問
3、修改客戶端程序 TestClient.java,增加訪問用戶名、密碼(新加的部分已標(biāo)出)
TestClient.java
import org.apache.axis.client.Service;
import javax.xml.rpc.ParameterMode;
{
public static void main(String [] args) throws Exception {
String endpoint = "http://localhost:" +"8080"+ "/axis/HelloWorld";
Call call = (Call) service.createCall();
call.getMessageContext().setUsername("ronghao");// 用戶名。
call.getMessageContext().setPassword("1111");// 密碼
call.setTargetEndpointAddress( new java.net.URL(endpoint) );
String res = (String) call.invoke( new Object[] {} );
}
}
執(zhí)行TestClient,能夠順利訪問Web服務(wù);如果修改用戶名或者密碼,那么就不能訪問 。同樣,
你在http://localhost:8080/axis/SOAPMonitor中看到的請求和響應(yīng)的XML是明文!
Web服務(wù)也可以使用SSL作為傳輸協(xié)議。雖然JAX-RPC并沒有強制規(guī)定是否使用SSL協(xié)議,但在tomcat
下使用HTTPS協(xié)議。
1、使用JDK自帶的工具創(chuàng)建密匙庫和信任庫。
keytool -genkey -alias Server -keystore server.keystore -keyalg RSA
輸入keystore密碼: changeit
您的名字與姓氏是什么?
[Unknown]: Server
您的組織單位名稱是什么?
[Unknown]: ec
您的組織名稱是什么?
[Unknown]: ec
您所在的城市或區(qū)域名稱是什么?
[Unknown]: beijing
您所在的州或省份名稱是什么?
[Unknown]: beijing
該單位的兩字母國家代碼是什么
[Unknown]: CN
CN=Server, OU=ec, O=ec, L=beijing, ST=beijing, C=CN 正確嗎?
[否]: y
(如果和 keystore 密碼相同,按回車):
以上命令執(zhí)行完成后,將獲得一個名為server.keystore的密匙庫。
2)生成客戶端的信任庫。首先輸出RSA證書:
keytool -export -alias Server -file test_axis.cer -storepass changeit -keystore server.keystore
然后把RSA證書輸入到一個新的信任庫文件中。這個信任庫被客戶端使用,被用來驗證服務(wù)器端的身份。
keytool -import -file test_axis.cer -storepass changeit -keystore client.truststore -alias serverkey -noprompt
以上命令執(zhí)行完成后,將獲得一個名為client.truststore的信任庫。
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 -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 -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)在我們就有了四個文件:server.keystore,server.truststore,client.keystore,client.truststore
<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" />
<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 static void main(String [] args) throws Exception {
String endpoint = "https://localhost:" +"8440"+ "/axis/HelloWorld";//注意區(qū)別在這里!https!
Call call = (Call) service.createCall();
call.setTargetEndpointAddress( new java.net.URL(endpoint) );
String res = (String) call.invoke( new Object[] {} );
}
}
-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)
我們打算用Handler結(jié)合WSSecurity實現(xiàn)Web服務(wù)安全(Handler的有關(guān)內(nèi)容請參閱AXIS學(xué)習(xí)筆記(二))
設(shè)想流程:用WSClientRequestHandler.java位于客戶端對客戶端發(fā)出的XML文檔進行加密
WSServerRequestHandler.java位于服務(wù)器端對客戶端發(fā)出的加密后的XML文檔進行解密
WSServerResponseHandler.java位于服務(wù)器端對服務(wù)器端返回的XML文檔進行加密
WSClientResponseHandler.java位于客戶端對服務(wù)器端返回的XML文檔進行解密
1、使用ISNetworks安全提供者,ISNetworks實現(xiàn)了RSA加密、解密算法。
當(dāng)然,你也可以使用其它的安全提供者,并且可以使用不同的加密算法。
ISNetworks相關(guān)包ISNetworksProvider.jar??截惖?TOMCAT_HOME% \webapps\axis\WEB-INF\lib
2、Trust Services Integration Kit提供了一個WS-Security實現(xiàn)。你可以從http://www.xmltrustcenter.org獲得相關(guān)庫文件,分別是ws-security.jar和tsik.jar。ws-security.jar中包含一個WSSecurity類,我們使用它來對XML進行數(shù)字簽名和驗證,加密與解密。同樣拷貝到%TOMCAT_HOME%\webapps\axis\WEB-INF\lib
4、框架結(jié)構(gòu)
WSClientHandler.java //基類,包含了一些公用方法
WSClientRequestHandler.java //繼承于WSClientHandler.java,調(diào)用WSHelper.java對客戶端發(fā)出的XML文檔進行加密
WSClientResponseHandler.java //繼承于WSClientHandler.java,調(diào)用WSHelper.java對服務(wù)器端返回的XML文檔進行解密
WSServerHandler.java //基類,包含了一些公用方法
WSServerRequestHandler.java //繼承于WSServerHandler.java,調(diào)用WSHelper.java對客戶端發(fā)出的加密后的XML文檔進行解密
WSServerResponseHandler.java//繼承于WSServerHandler.java,調(diào)用WSHelper.java對服務(wù)器端返回的XML文檔進行加密
WSHelper.java //核心類,對SOAP消息簽名、加密、解密、身份驗證
MessageConverter.java //幫助類,Document、SOAP消息互相轉(zhuǎn)換
WSHelper.java
public class WSHelper {
static String PROVIDER="ISNetworks";//JSSE安全提供者。
//添加JSSE安全提供者,你也可以使用其它安全提供者。只要支持DESede算法。這是程序里動態(tài)加載還可以在JDK中靜態(tài)加載
static
{
java.security.Security.addProvider(new com.isnetworks.provider.jce.ISNetworksProvider());
}
/**
*對XML文檔進行數(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);//簽名。
}
/**
*對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());
//解密。
WsUtils.removeEncryptedKey(doc);//從 WS-Security Header中刪除 EncryptedKey 元素
}
WsUtils.removeWSSElements(doc);// 刪除WSS相關(guān)的元素。
}
//繼承自org.apache.axis.handlers.BasicHandler即AXIS內(nèi)在的
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 ;
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 {//在這個方法里對XML文檔進行處理
//do nothing now!
}
public void onFault(MessageContext msgContext) {
System.out.println("處理錯誤,這里忽略!");
}
}
public class WSClientRequestHandler extends WSClientHandler{
public void invoke(MessageContext messageContext) throws AxisFault {
try {
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)時發(fā)生以下錯誤: " + e);
e.printStackTrace(); }
}
}
public class WSClientResponseHandler extends WSClientHandler{
public void invoke(MessageContext messageContext) throws AxisFault {
try {
Document doc = MessageConverter.convertSoapMessageToDocument(soapMessage);
keyStorePassword, keyAlias, keyEntryPassword);//解密
WSHelper.removeWSSElements(doc);
soapMessage = MessageConverter.convertDocumentToSOAPMessage(doc);
messageContext.setMessage(soapMessage);
} catch (Exception e){
e.printStackTrace();
System.err.println("在處理響應(yīng)時發(fā)生以下錯誤: " + e);
}
}
WSServerHandler.java
public class WSServerHandler 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 ;
//do nothing now!
}
public void onFault(MessageContext msgContext) {
System.out.println("處理錯誤,這里忽略!");
}
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.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)時發(fā)生以下錯誤: " + e);
}
}
WSServerResponseHandler.java
public class WSServerResponseHandler extends WSServerHandler{
public void invoke(MessageContext messageContext) throws AxisFault {
try {
System.out.println("返回的原始消息:");
soapMessage.writeTo(System.out);
Document doc = MessageConverter.convertSoapMessageToDocument(soapMessage);
keyStorePassword, keyAlias, keyEntryPassword);//數(shù)字簽名
WSHelper.encrypt(doc, trustStoreFile, trustStoreType,//加密
trustStorePassword, certAlias);
System.out.println("返回的加密后的消息:");
soapMessage.writeTo(System.out);
messageContext.setMessage(soapMessage);
} catch (Exception e){
System.err.println("在處理響應(yīng)時發(fā)生以下錯誤: " + e);
e.printStackTrace();
}
}
為方便使用,把上述文件打包為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();
//注意新加的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"));
System.out.println("the result"+result);
e.printStackTrace();
}
}
}
運行的時候http://localhost:8080/axis/SOAPMonitor中看到的請求的XML就已加密!
總結(jié)
這里對代碼的解釋是不夠的,很多概念沒有提到。建議你最好看tsik.jar和AXIS的API深入了解。另外對ws-axis.jar的加
++++++++++++++++++++++++++++++++++++++
AXIS學(xué)習(xí)筆記(五)( 在AXIS服務(wù)間傳遞JavaBean及其安全解決)
ronghao100 原創(chuàng)
在AXIS服務(wù)間傳遞JavaBean及其安全解決
這是AXIS學(xué)習(xí)筆記的最后一篇。在前面我們討論了最簡單的HelloWorld服務(wù),客戶端并沒有向服務(wù)器端
傳遞參數(shù),現(xiàn)在我們來傳傳JavaBean。當(dāng)然,也可以傳遞你自己定義的JAVA類,但那樣你必須自己創(chuàng)建
專門的XML序列化器和反序列化器;而對JavaBean,AXIS提供了現(xiàn)成的序列化器。(有人說:懶惰是程序員最大的美德,我喜歡,所以我就傳傳JavaBean)
1、CLASS類兩個Order.class,OrderTest.class,位于%TOMCAT_HOME%\webapps\axis\WEB-INF\classes下
這兩個類都直接給出源碼,不再說明
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類的名字
其他是固定的。
二、客戶端
客戶端類文件一個OrderClient.class,代碼如下(變化的部分加注釋):
public class OrderClient
{
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();
//注冊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!運行一下,就可以看到返回了"ronghao"。
在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>首先在這里要說一下,客戶端代碼編寫困擾了我很長一段時間(整整一天),因為它并不象我想象的那么簡單,當(dāng)然解決起來還是挺簡單的:)問題的解決經(jīng)歷了三個階段
在這個階段我想當(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
這個方法也是我在上一文章里介紹的,結(jié)果拋出以下異常:
faultString: org.xml.sax.SAXException: Deserializing parameter
'newProfileReturn': could not find deserializer for type
{urn:BeanService Order}SerializableProfile
也就是說不能正常解析XML文件,于是理所當(dāng)然的郁悶了,覺得代碼中肯定漏設(shè)了CALL的一個屬性,于是查看AXIS的源代碼,沒有結(jié)果!轉(zhuǎn)機出現(xiàn)在下面一行代碼,在不斷的拋出異常中我修改了代碼
將call.setClientHandlers(handler,handlee);改為
call.setClientHandlers(null,null);
結(jié)果程序還是拋出同樣的異常,于是意識到這可能是AXIS的一個BUG,為證明這一點,我將下面的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!得到這個結(jié)論后去了apache AXIS主頁,在問題列表中見到了完全一樣問題的提交,但沒有解答(暈?。?br> 最后得到了結(jié)論:call的setClientHandlers()方法只有當(dāng)call處理簡單的數(shù)據(jù)類型,如String,int等等才能正常使用!
(當(dāng)然,如果你對這個問題有不同的見解,歡迎和我聯(lián)系。或許我錯了,但程序不運行是真的:))
第二階段:
開始在google上找問題的解決方法,這也是我的習(xí)慣:)。找了一個類似問題的討論,地址如下:
http://marc.theaimsgroup.com/?l=axis-user&m=111259980822735&w=2
他們的解決方法是Handler繼承于javax.xml.rpc.handler.Handler,然后在程序里動態(tài)注冊而在我的ws-axis.jar里Handler繼承于org.apache.axis.handlers.BasicHandler。當(dāng)然,
javax.xml.rpc.handler.Handler是org.apache.axis.handlers.BasicHandler的老爸,但在程序里老爸和兒子之間卻不能很好的兼容,這也許就是所謂的代溝??無奈中重新寫了Handler,但在運行中卻拋出異常,提示message在被invoke的時候已被更改。我靠,Handler的作用就是來更改message的啊!這是什么世道!
我知道很多程序采用的就是這種方法,但我好象怎么修改都拋出上述異常。
第三階段
既然在程序里動態(tài)注冊Handler行不通,于是決定寫個單獨的配置文件來注冊Handler。如果這種方法不幸失敗就返回第二階段。好馬為什么不吃回頭草??
1、ws-axis.jar中修改WSClientHandler.class,修改后如下,我想你一看就明白為何修改
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 ;
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("處理錯誤,這里忽略!");
}
}
2、寫
<?xml version="1.0" encoding="UTF-8"?>
<deployment name="defaultClientConfig"
xmlns="
<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"/>
<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>
同樣不再解釋,不明白可以參考我的上一篇文章
在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;
運行一下,返回"ronghao",靠,搞定!
注意:這次我把OrderClient.class的調(diào)用放到了一個JSP文件中而不是jbuilder中,因為有client-config.wsdd,所以你必須有完整的WEB程序發(fā)布到TOMCAT中,否則會報找不到
相應(yīng)文件。