http://www.silvery-lunar.com/simple/index.php?t295.html
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服务进行交互,它通常称为Provider。axis中常用的Provider有Java:RPC,java:MSG,java:EJB。一个Web服务可以部署一个或者多个Handler。
Apache axis中的Handler体系结构和JAX-RPC 1.0(JSR101)中的体系结构稍有不同,需要声明的是,本文的代码在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的部分代码
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; … }
为了提供开发的方便,在编写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中实现。 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所示。
例程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!", 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 服务 " + 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代码如下:
例程5 认证的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所示。
图2 访问web服务时的验证
如果客户端是应用程序,那么可以这样在客户端设置用户名和密码:
例程6 在客户端设置用户名和密码
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");
|