AXIS服務間傳遞JavaBean及其安全解決

這是AXIS學習筆記的最後一篇。在前面我們討論了最簡單的HelloWorld服務,客戶端並沒有向服務器端傳遞參數,現在我們來傳JavaBean。當然,也可以傳遞你自己定義的JAVA類,但那樣你必須自己創建專門的XML序列化器和反序列化器;而對JavaBean,AXIS提供了現成的序列化器。(有人說:懶惰是程序員最大的美德)
 
 一、服務器端
  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、修改服務器端配置文件server-config.wsdd
   
server-config.wsdd中相應位置添加以下代碼
  <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>

  可以看到和前面的發佈服務代碼相比僅多了一行代碼
    <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
{

    
public static void main(String args[])
        
throws Exception
    {
        String endpoint 
= "http://localhost:8080/axis/services/Order";  //服務所在位置
        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));
            
//調用的服務器端方法
            call.setOperationName(new QName("Order""returnOrder"));
            
//設定傳入的參數,這裏qn即Order.class
            call.addParameter("arg1", qn, ParameterMode.IN);
            
//設定返回的參數是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"
  
  
和上一篇文章一樣,我們不容許在網絡中傳遞XML是明文,於是需要加密和驗證。這裏我們繼續採用上次所講的框架。(已打包成ws-axis.jar)
  
 
一、修改服務器端配置文件server-config.wsdd(和上一文章一模一樣!不再羅嗦)
  
server-config.wsdd中相應位置添加以下代碼
         <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>

        
 二、客戶端(區別就在這裏,注意!!)
  
首先在這裏要說一下,客戶端代碼編寫困擾了我很長一段時間(整整一天),因爲它並不象我想象的那麼簡單,當然解決起來還是挺簡單的:)問題的解決經歷了三個階段
  
  
第一階段:
   
在這個階段我想當然的在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

   這個方法也是我在上一文章裏介紹的,結果拋出以下異常:
   faultString: org.xml.sax.SAXException: Deserializing parameter
    
&apos;newProfileReturn&apos;:  could not find deserializer for type
   {urn:BeanService Order}SerializableProfile

   也就是說不能正常解析XML文件,於是理所當然的鬱悶了,覺得代碼中肯定漏設了CALL的一個屬性,於是查看AXIS的源代碼,沒有結果!轉機出現在下面一行代碼,在不斷的拋出異常中我修改了代碼
   
call.setClientHandlers(handler,handlee);改爲
     call.setClientHandlers(null,null);
   
結果程序還是拋出同樣的異常,於是意識到這可能是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");//初始化

   結果還是拋出同樣的異常,果然是BUG!得到這個結論後去了apache AXIS主頁,在問題列表中見到了完全一樣問題的提交,但沒有解答(暈!)
   
最後得到了結論:call的setClientHandlers()方法只有當call處理簡單的數據類型,如String,int等等才能正常使用!
   
(當然,如果你對這個問題有不同的見解,歡迎和我聯繫。或許我錯了,但程序不運行是真的:))
   
  
第二階段:
    
開始在google上找問題的解決方法,這也是我的習慣:)。找了一個類似問題的討論,地址如下:
    http://marc.theaimsgroup.com/?l=axis-user&m=111259980822735&w=2
    
他們的解決方法是Handler繼承於javax.xml.rpc.handler.Handler,然後在程序裏動態註冊而在我的ws-axis.jarHandler繼承於org.apache.axis.handlers.BasicHandler。當然,
    javax.xml.rpc.handler.Handler
org.apache.axis.handlers.BasicHandler的老爸,但在程序里老爸和兒子之間卻不能很好的兼容,這也許就是所謂的代溝??無奈中重新寫了Handler,但在運行中卻拋出異常,提示message在被invoke的時候已被更改。Handler的作用就是來更改message的啊!
    
我知道很多程序採用的就是這種方法,但我好象怎麼修改都拋出上述異常。
    
  
第三階段
     
既然在程序裏動態註冊Handler行不通,於是決定寫個單獨的配置文件來註冊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(
"處理錯誤,這裏忽略!");
        }
}

    
  2
、寫客戶端的配置代碼client-config.wsdd,如下:
   <?xml version="1.0" encoding="UTF-8"?>
   
<deployment name="defaultClientConfig"
      xmlns
="http://xml.apache.org/axis/wsdd/"
      xmlns:java
="http://xml.apache.org/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);

   當然記得導入
   import org.apache.axis.EngineConfiguration;
   
import org.apache.axis.configuration.FileProvider;

   
 運行一下,返回"ronghao",搞定!
 
注意:這次我把OrderClient.class的調用放到了一個JSP文件中而不是jbuilder中,因爲有client-config.wsdd,所以你必須有完整的WEB程序發佈到TOMCAT中,否則會報找不到
相應文件。

http://www.blogjava.net/ronghao 榮浩原創,轉載請註明出處:)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章