现在的位置: 首页 > 综合 > 正文

使用Handler来增强Web服务的功能

2013年02月02日 ⁄ 综合 ⁄ 共 10629字 ⁄ 字号 评论关闭

原文作者: 陈亚强

原文链接:http://www.ibm.com/developerworks/cn/webservices/ws-handler/index.html
高级软件工程师北京华园天一科技有限公司 2003  8  

一、Handler的基本概念

J2EE Web 服务中的Handler技术特点非常像Servlet技术中的Filter。我们知道,在Servlet中,当一个HTTP到达服务端时,往往要经过多个Filter对请求进行过滤,然后才到达提供服务的Servlet,这些Filter的功能往往是对请求进行统一编码,对用户进行认证,把用户的访问写入系统日志等。相应的,Web服务中的Handler通常也提供一下的功能: 

对客户端进行认证、授权; 
把用户的访问写入系统日志; 
对请求的SOAP消息进行加密,解密; 
Web Services对象做缓存。 
SOAP
消息Handler能够访问代表RPC请求或者响应的SOAP消息。在JAX-RPC技术中,SOAP消息Handler可以部署在服务端,也可以在客户端使用。 

下面我们来看一个典型的SOAP消息Handler处理顺序: 
某个在线支付服务需要防止非授权的用户访问或者撰改服务端和客户端传输的信息,从而使用消息摘要(Message Digest)的方法对请求和响应的SOAP消息进行加密。当客户端发送SOAP?Ф?andler把请求消息中的某些敏感的信息(如信用卡密码)进行加密,然后把加密后的SOAP消息传输到服务端;服务端的SOAP消息Handler截取客户端的请求,把请求的SOAP 消息进行解密,然后把解密后的SOAP消息派发到目标的Web服务端点。 

Apache axis是我们当前开发Web服务的较好的选择,使用axisWeb服务开发工具,可以使用Handler来对服务端的请求和响应进行处理。典型的情况下,请求传递如图1所示。 


1 SOAP消息的传递顺序

在图中,轴心点(pivot point)是Apache与提供程序功能相当的部分,通过它来和目标的Web服务进行交互,它通常称为Provideraxis中常用的ProviderJavaRPCjavaMSGjavaEJB。一个Web服务可以部署一个或者多个Handler 

Apache axis中的Handler体系结构和JAX-RPC 1.0JSR101)中的体系结构稍有不同,需要声明的是,本文的代码在axis中开发,故需要在axis环境下运行。 

axis环境下,SOAP消息Handler必须实现org.apache.axis.Handler接口(在JAX-RPC 1.0规范中,SOAP消息Handler必须实现javax.xml.rpc.handler.Handler接口),org.apache.axis.Handler接口的部分代码如下: 

例程1 org.apache.axis.Handle的部分代码
为了提供开发的方便,在编写Handler时,只要继承org.apache.axis.handlers. BasicHandler即可,BasicHandlerHandler的一个模板,我们看它的部分代码: 

例程2 BasicHandler的部分代码


public abstract class BasicHandler implements Handler {
    
protected static Log log =
        LogFactory.getLog(BasicHandler.
class.getName());
    
protected Hashtable options;
    
protected String name;
    
//这个方法必须在Handler中实现。
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实现类必须实现的方法,它通过MessageContext来获得请求或者响应的SOAPMessage对象,然后对SOAPMessage进行处理。 

在介绍Handler的开发之前,我们先来看一下目标Web服务的端点实现类的代码,如例程3所示。 

例程目标Web服务的端点实现类

package com.hellking.webservice;
public class HandleredService 
{
 
//一个简单的Web服务
 public String publicMethod(String name)
 {
  
return "Hello!"+name;
 }
}
//另一个Web服务端点:
package com.hellking.webservice;
public class OrderService 
{
       
//web服务方法:获得客户端的订单信息,并且对订单信息进行对应的处理,
通常情况是把订单的信息写入数据库,然后可客户端返回确认信息。
 
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("这里是客户端发送来的信息:");
  System.out.println(orderInfo);  
  
return orderInfo;
 } 
}

 
二、下面我们分不同情况讨论Handler的使用实例。

使用Handler为系统做日志

Handler为系统做日志是一种比较常见而且简单的使用方式。和Servlet中的Filter一样,我们可以使用Handler来把用户的访问写入系统日志。下面我们来看日志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都必须实现的方法。
  
*/
    
public void invoke(MessageContext msgContext) throws AxisFault
    {
       
//每当web服务被调用,都记录到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!",
                                    
nullnull);
            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 服务 " +
                            msgContext.getTargetService() 
+
                            
" 被调用,现在已经共调用了 " + counter + " 次.";
            handler.setOption(
"accesses", counter);            
            writer.println(result);            
            writer.close();
        } 
catch (Exception e) {
            
throw AxisFault.makeFault(e);
        }
    }
}

 

前面我们说过,Handler实现类必须实现invoke方法,invoke方法是Handler处理其业务的入口点。LogHandler的主要功能是把客户端访问的Web服务的名称和访问时间、访问的次数记录到一个日志文件中。 

下面部署这个前面开发的Web服务对像,然后为Web服务指定Handler。编辑Axis_Home/WEB-INF/ server-config.wsdd文件,在其中加入以下的内容:

<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需要根据具体情况改变。
Sun Jul 06 22:42:03 CST 2003: Web 服务 HandleredService 被调用,现在已经共调用了 1 .
Sun Jul 06 22:42:06 CST 2003: Web 服务 HandleredService 被调用,现在已经共调用了 2 .
Sun Jul 06 22:42:13 CST 2003: Web 服务 HandleredService 被调用,现在已经共调用了 3 .

使用Handler对用户的访问认证

使用Handler为用户访问认证也是它的典型使用,通过它,可以减少在Web服务端代码中认证的麻烦,同时可以在部署描述符中灵活改变用户的访问权限。 

对用户认证的Handler代码如下:

例程认证的Handler

package com.hellking.webservice;
import….

//此handler的目的是对用户认证,只有认证的用户才能访问目标服务。
public class AuthenticationHandler extends BasicHandler
{
 
/**invoke,每一个handler都必须实现的方法。
  
*/
 
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);
            
//用户通过认证,把用户的设置成认证了的用户。
            msgContext.setProperty("authenticatedUser", authUser);
        } 
    }
}

AuthenticationHandler代码里,它从MessageContext中获得用户信息,然后进行认证,如果认证成功,那么就使用msgContext.setProperty("authenticatedUser", authUser)方法把用户设置成认证了的用户,如果认证不成功,那么就抛出Server.Unauthenticated异常。 

部署这个Handler,同样,在server-config里加入以下的内容:


<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所示。

<!--[if !vml]--><!--[endif]-->
 

访问web服务时的验证 

如果客户端是应用程序,那么可以这样在客户端设置用户名和密码:

例程在客户端设置用户名和密码

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") );//设置操作的名称。
            
//由于需要认证,故需要设置调用的用户名和密码。
            call.getMessageContext().setUsername("chen");
            call.getMessageContext().setPassword(
"chen");  

 

使用Handler对用户的访问授权

对于已经认证了的用户,有时在他们操作某个特定的服务时,还需要进行授权,只有授权的用户才能继续进行操作。我们看对用户进行授权的Handler的代码。 

例程对用户进行授权的代码

package com.hellking.webservice;

import

//此handler的目的是对认证的用户授权,只有授权的用户才能访问目标服务。
public class AuthorizationHandler extends BasicHandler
{
 
/**invoke,每一个handler都必须实现的方法。
  
*/
 
public void invoke(MessageContext msgContext)
        
throws AxisFault
    {
      
        AuthenticatedUser user 
= (AuthenticatedUser)msgContext.getProperty("authenticatedUser");
        
if(user == null)
            
throw new AxisFault("Server.NoUser", Messages.getMessage("needUser00"), nullnull);
        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;//访问授权通过。
            }
        }
        
//没有通过授权,不能访问目标服务,抛出Server.Unauthorized异常。
        throw new AxisFault("Server.Unauthorized"
Messages.getMessage(
"cantAuth02", userId, serviceName), nullnull);
    }     
}

service-config.wsdd文件中,我们为Web服务指定了以下的用户:


<parameter name="allowedRoles" value="chen,hellking"/>

provider.userMatches(user, thisRole)将匹配允许访问Web服务的用户,如果匹配成功,那么授权通过,如果没有授权成功,那么抛出Server.Unauthorized异常。 

使用HandlerSOAP消息进行加密、解密

由于SOAP消息在HTTP协议中传输,而HTTP协议的安全度是比较低的,怎么保证信息安全到达对方而不泄漏或中途被撰改,将是Web服务必须解决的问题。围绕Web服务的安全,有很多相关的技术,比如WS-SecurityWS-Trace等,另外,还有以下相关技术: 

XML Digital SignatureXML数字签名) 
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 
不管使用什么技术,要使信息安全到达对方,必须把它进行加密,然后在对方收到信息后解密。为了提供开发的方便,可以使用Handler技术,在客户端发送信息前,使用客户端的HandlerSOAP

抱歉!评论已关闭.