Hessian源码分析和Hack --让Hessian携带远程调用端的信息(下)

接着说Spring包装过的Hessian怎么来Hack。 
刚开始我以为只要改了hessian的源码,就可以了。其实不然,因为Spring通过了几层包装,让你不能得不到request对象。 
Spring里边hessian的入口是HessianServiceExporter这个类,主要方法有两个 

Java代码   收藏代码
  1. public void prepare() {  
  2.     HessianSkeleton skeleton = null;  
  3.   
  4.     try {  
  5.         try {  
  6.             // Try Hessian 3.x (with service interface argument).  
  7.             Constructor ctor = HessianSkeleton.class.getConstructor(new Class[] {Object.class, Class.class});  
  8.             checkService();  
  9.             checkServiceInterface();  
  10.             skeleton = (HessianSkeleton)  
  11.                     ctor.newInstance(new Object[] {getProxyForService(), getServiceInterface()});  
  12.         }  
  13.         catch (NoSuchMethodException ex) {  
  14.             // Fall back to Hessian 2.x (without service interface argument).  
  15.             Constructor ctor = HessianSkeleton.class.getConstructor(new Class[] {Object.class});  
  16.             skeleton = (HessianSkeleton) ctor.newInstance(new Object[] {getProxyForService()});  
  17.         }  
  18.     }  
  19.     catch (Throwable ex) {  
  20.         throw new BeanInitializationException("Hessian skeleton initialization failed", ex);  
  21.     }  
  22.   
  23.     if (hessian2Available) {  
  24.         // Hessian 2 (version 3.0.20+).  
  25.         this.skeletonInvoker = new Hessian2SkeletonInvoker(skeleton, this.serializerFactory);  
  26.     }  
  27.     else {  
  28.         // Hessian 1 (version 3.0.19-).  
  29.         this.skeletonInvoker = new Hessian1SkeletonInvoker(skeleton, this.serializerFactory);  
  30.     }  
  31. }  
  32.   
  33.   
  34. /** 
  35.  * Processes the incoming Hessian request and creates a Hessian response. 
  36.  */  
  37. public void handleRequest(HttpServletRequest request, HttpServletResponse response)  
  38.         throws ServletException, IOException {  
  39.   
  40.     Assert.notNull(this.skeletonInvoker, "HessianServiceExporter has not been initialized");  
  41.             if (!"POST".equals(request.getMethod())) {  
  42.         throw new HttpRequestMethodNotSupportedException(  
  43.                 "POST""HessianServiceExporter only supports POST requests");  
  44.     }  
  45.   
  46.     try {  
  47.       this.skeletonInvoker.invoke(request.getInputStream(), response.getOutputStream());  
  48.     }  
  49.     catch (Throwable ex) {  
  50.       throw new NestedServletException("Hessian skeleton invocation failed", ex);  
  51.     }finally{  
  52.         ServiceContext.end();  
  53.     }  
  54. }  



prepare()方法是在Spring环境加载的时候执行的,用来初始化Hessian的HessianSkeleton并包装成Spring自己的Hessian2SkeletonInvoker。 
handleRequest()方法是每次远程调用的入口,里边的这个方法执行后续hessian的解析流程 

Java代码   收藏代码
  1. this.skeletonInvoker.invoke(request.getInputStream(), response.getOutputStream());  


skeletonInvoker也就是prepare里初始化的Hessian2SkeletonInvoker,跟进去看看先 

Java代码   收藏代码
  1. class Hessian2SkeletonInvoker extends HessianSkeletonInvoker {  
  2.   
  3.     public Hessian2SkeletonInvoker(HessianSkeleton skeleton, SerializerFactory serializerFactory) {  
  4.         super(skeleton, serializerFactory);  
  5.     }  
  6.   
  7.     public void invoke(InputStream inputStream, OutputStream outputStream) throws Throwable {  
  8.         Hessian2Input in = new Hessian2Input(inputStream);  
  9.         if (this.serializerFactory != null) {  
  10.             in.setSerializerFactory(this.serializerFactory);  
  11.         }  
  12.   
  13.         int code = in.read();  
  14.         if (code != 'c') {  
  15.             throw new IOException("expected 'c' in hessian input at " + code);  
  16.         }  
  17.   
  18.         AbstractHessianOutput out = null;  
  19.         int major = in.read();  
  20.         int minor = in.read();  
  21.         if (major >= 2) {  
  22.             out = new Hessian2Output(outputStream);  
  23.         }  
  24.         else {  
  25.             out = new HessianOutput(outputStream);  
  26.         }  
  27.         if (this.serializerFactory != null) {  
  28.             out.setSerializerFactory(this.serializerFactory);  
  29.         }  
  30.   
  31.         this.skeleton.invoke(in, out);  
  32.     }  
  33.   
  34. }  


Hessian2SkeletonInvoker的构造函数进行父类的初始化,也就是初始化了最重要的HessianSkeleton 
再看invoke()方法,是不是有点眼熟,对了,这就是hessian源代码里边的以部分代码,用来包装输入输入流为Hessian自己的Hessian2Input 等,然后判断开头的字节是否正确,以及确定HessianOutput的版本。这些都是解析输入流里边的字节标志位来实现的。 在方法的最后调用了

Java代码   收藏代码
  1. this.skeleton.invoke(in, out);  

,也就是把控制权交给了Hessian来处理,这样后续的工作就是Hessian中完成。但是这样就ok了吗?慢着,我们是不是漏了点什么? 对,那个ServiceContext上下文。因为Hessian是在自己的入口Servlet中生成上下文的,但是Spring并没有生成这个东西。所以,我在这里自己生成了它,唯一一个能够生成的地方就在Spring的HessianServiceExporter的handleRequest()里边,只有在这里才有request对象。因此我把handleRequest()方法中加了一句ServiceContext.begin(request, null, null);然后在finally里边加了一句ServiceContext.end(); 
代码如下: 

Java代码   收藏代码
  1. public void handleRequest(HttpServletRequest request, HttpServletResponse response)  
  2.         throws ServletException, IOException {  
  3.   
  4.     Assert.notNull(this.skeletonInvoker, "HessianServiceExporter has not been initialized");  
  5.     ServiceContext.begin(request, nullnull);  
  6.     if (!"POST".equals(request.getMethod())) {  
  7.         throw new HttpRequestMethodNotSupportedException(  
  8.                 "POST""HessianServiceExporter only supports POST requests");  
  9.     }  
  10.   
  11.     try {  
  12.       this.skeletonInvoker.invoke(request.getInputStream(), response.getOutputStream());  
  13.     }  
  14.     catch (Throwable ex) {  
  15.       throw new NestedServletException("Hessian skeleton invocation failed", ex);  
  16.     }finally{  
  17.         ServiceContext.end();  
  18.     }  
  19. }  



ServiceContext.begin()方法生成一个ThreadLocal对象,ServiceContext.end()则释放它里边的上下文。 
加了这个地方,就可以用上面的hessian源码进行剩余操作了 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值