读书人

Hessian源码分析跟Hack -让Hessian携带

发布时间: 2012-10-23 12:12:22 作者: rapoo

Hessian源码分析和Hack --让Hessian携带远程调用端的信息
项目选定Hessian作为web service的实现方式,确实很轻量级,速度就跟直接用socket差不多,全是二进制传送节约了不少开销。但是在使用过程中有业务需要是必须获得远程端的ip地址,主机名等信息的。翻便Hessian的文档和google了n次未果,迫不得已到caucho和spring论坛去问,都没有得到答复。今天心一横把hessian的源代码加入到项目中单步跟踪,总算有点小收获。献丑分享出来,一方面给需要的朋友,主要还是希望各位找找是否存在bug,以及是否有更好的改良。

一:先撇开Spring不谈,来看看纯Hessian的调用
按照hessian文档里边介绍的demo,在web.xml里边如下配置

<servlet>    <servlet-name>hello</servlet-name>    <servlet-class>com.caucho.hessian.server.HessianServlet</servlet-class>     <init-param>       <param-name>home-class</param-name>       <param-value>example.BasicService</param-value>     </init-param>     <init-param>       <param-name>home-api</param-name>       <param-value>example.Basic</param-value>     </init-param>   </servlet>     <servlet-mapping>     <url-pattern>/hello</url-pattern>     <servlet-name>hello</servlet-name>   </servlet-mapping>     <servlet>   <servlet-name>hello</servlet-name>   <servlet-class>com.caucho.hessian.server.HessianServlet</servlet-class>    <init-param>      <param-name>home-class</param-name>      <param-value>example.BasicService</param-value>    </init-param>    <init-param>      <param-name>home-api</param-name>      <param-value>example.Basic</param-value>    </init-param>  </servlet>  <servlet-mapping>    <url-pattern>/hello</url-pattern>    <servlet-name>hello</servlet-name>  </servlet-mapping>



由此可知Hessian调用的入口是HessianServlet这个Servlet,进去看看


/**   * Servlet for serving Hessian services.   */  public class HessianServlet extends GenericServlet {     private Class _homeAPI;     private Object _homeImpl;          private Class _objectAPI;     private Object _objectImpl;          private HessianSkeleton _homeSkeleton;     private HessianSkeleton _objectSkeleton;       private SerializerFactory _serializerFactory;       public String getServletInfo()     {       return "Hessian Servlet";     }       /**     * Sets the home api.     */    public void setHomeAPI(Class api)     {       _homeAPI = api;     }       /**     * Sets the home implementation     */    public void setHome(Object home)     {       _homeImpl = home;     }       /**     * Sets the object api.     */    public void setObjectAPI(Class api)     {       _objectAPI = api;     }       /**     * Sets the object implementation     */    public void setObject(Object object)     {       _objectImpl = object;     }       /**     * Sets the service class.     */    public void setService(Object service)     {       setHome(service);     }       /**     * Sets the api-class.     */    public void setAPIClass(Class api)     {       setHomeAPI(api);     }       /**     * Gets the api-class.     */    public Class getAPIClass()     {       return _homeAPI;     }       /**     * Sets the serializer factory.     */    public void setSerializerFactory(SerializerFactory factory)     {       _serializerFactory = factory;     }       /**     * Gets the serializer factory.     */    public SerializerFactory getSerializerFactory()     {       if (_serializerFactory == null)         _serializerFactory = new SerializerFactory();         return _serializerFactory;     }       /**     * Sets the serializer send collection java type.     */    public void setSendCollectionType(boolean sendType)     {       getSerializerFactory().setSendCollectionType(sendType);     }       /**     * Initialize the service, including the service object.     */    public void init(ServletConfig config)       throws ServletException     {       super.init(config);              try {         if (_homeImpl != null) {         }         else if (getInitParameter("home-class") != null) {       String className = getInitParameter("home-class");              Class homeClass = loadClass(className);         _homeImpl = homeClass.newInstance();         init(_homeImpl);         }         else if (getInitParameter("service-class") != null) {       String className = getInitParameter("service-class");              Class homeClass = loadClass(className);         _homeImpl = homeClass.newInstance();              init(_homeImpl);         }         else {       if (getClass().equals(HessianServlet.class))         throw new ServletException("server must extend HessianServlet");         _homeImpl = this;         }           if (_homeAPI != null) {         }         else if (getInitParameter("home-api") != null) {       String className = getInitParameter("home-api");              _homeAPI = loadClass(className);         }         else if (getInitParameter("api-class") != null) {       String className = getInitParameter("api-class");         _homeAPI = loadClass(className);         }         else if (_homeImpl != null) {       _homeAPI = findRemoteAPI(_homeImpl.getClass());         if (_homeAPI == null)         _homeAPI = _homeImpl.getClass();         }                  if (_objectImpl != null) {         }         else if (getInitParameter("object-class") != null) {       String className = getInitParameter("object-class");              Class objectClass = loadClass(className);         _objectImpl = objectClass.newInstance();         init(_objectImpl);         }           if (_objectAPI != null) {         }         else if (getInitParameter("object-api") != null) {       String className = getInitParameter("object-api");              _objectAPI = loadClass(className);         }         else if (_objectImpl != null)       _objectAPI = _objectImpl.getClass();           _homeSkeleton = new HessianSkeleton(_homeImpl, _homeAPI);         if (_objectAPI != null)       _homeSkeleton.setObjectClass(_objectAPI);           if (_objectImpl != null) {       _objectSkeleton = new HessianSkeleton(_objectImpl, _objectAPI);       _objectSkeleton.setHomeClass(_homeAPI);         }         else      _objectSkeleton = _homeSkeleton;       } catch (ServletException e) {         throw e;       } catch (Exception e) {         throw new ServletException(e);       }     }       private Class findRemoteAPI(Class implClass)     {       if (implClass == null || implClass.equals(GenericService.class))         return null;              Class []interfaces = implClass.getInterfaces();         if (interfaces.length == 1)         return interfaces[0];         return findRemoteAPI(implClass.getSuperclass());     }       private Class loadClass(String className)       throws ClassNotFoundException     {       ClassLoader loader = Thread.currentThread().getContextClassLoader();         if (loader != null)         return Class.forName(className, false, loader);       else        return Class.forName(className);     }       private void init(Object service)       throws ServletException     {       if (service instanceof Service)         ((Service) service).init(getServletConfig());       else if (service instanceof Servlet)         ((Servlet) service).init(getServletConfig());     }          /**     * Execute a request.  The path-info of the request selects the bean.     * Once the bean's selected, it will be applied.     */    public void service(ServletRequest request, ServletResponse response)       throws IOException, ServletException     {       HttpServletRequest req = (HttpServletRequest) request;       HttpServletResponse res = (HttpServletResponse) response;         if (! req.getMethod().equals("POST")) {         res.setStatus(500, "Hessian Requires POST");         PrintWriter out = res.getWriter();           res.setContentType("text/html");         out.println("<h1>Hessian Requires POST</h1>");                  return;       }         String serviceId = req.getPathInfo();       String objectId = req.getParameter("id");       if (objectId == null)         objectId = req.getParameter("ejbid");         ServiceContext.begin(req, serviceId, objectId);         try {         InputStream is = request.getInputStream();         OutputStream os = response.getOutputStream();           Hessian2Input in = new Hessian2Input(is);         AbstractHessianOutput out;           SerializerFactory serializerFactory = getSerializerFactory();                  in.setSerializerFactory(serializerFactory);           int code = in.read();           if (code != 'c') {       // XXX: deflate       throw new IOException("expected 'c' in hessian input at " + code);         }           int major = in.read();         int minor = in.read();           if (major >= 2)       out = new Hessian2Output(os);         else      out = new HessianOutput(os);                  out.setSerializerFactory(serializerFactory);           if (objectId != null)       _objectSkeleton.invoke(in, out);         else      _homeSkeleton.invoke(in, out);           out.close();       } catch (RuntimeException e) {         throw e;       } catch (ServletException e) {         throw e;       } catch (Throwable e) {         throw new ServletException(e);       } finally {         ServiceContext.end();       }     }   }  /** * Servlet for serving Hessian services. */public class HessianServlet extends GenericServlet {  private Class _homeAPI;  private Object _homeImpl;    private Class _objectAPI;  private Object _objectImpl;    private HessianSkeleton _homeSkeleton;  private HessianSkeleton _objectSkeleton;  private SerializerFactory _serializerFactory;  public String getServletInfo()  {    return "Hessian Servlet";  }  /**   * Sets the home api.   */  public void setHomeAPI(Class api)  {    _homeAPI = api;  }  /**   * Sets the home implementation   */  public void setHome(Object home)  {    _homeImpl = home;  }  /**   * Sets the object api.   */  public void setObjectAPI(Class api)  {    _objectAPI = api;  }  /**   * Sets the object implementation   */  public void setObject(Object object)  {    _objectImpl = object;  }  /**   * Sets the service class.   */  public void setService(Object service)  {    setHome(service);  }  /**   * Sets the api-class.   */  public void setAPIClass(Class api)  {    setHomeAPI(api);  }  /**   * Gets the api-class.   */  public Class getAPIClass()  {    return _homeAPI;  }  /**   * Sets the serializer factory.   */  public void setSerializerFactory(SerializerFactory factory)  {    _serializerFactory = factory;  }  /**   * Gets the serializer factory.   */  public SerializerFactory getSerializerFactory()  {    if (_serializerFactory == null)      _serializerFactory = new SerializerFactory();    return _serializerFactory;  }  /**   * Sets the serializer send collection java type.   */  public void setSendCollectionType(boolean sendType)  {    getSerializerFactory().setSendCollectionType(sendType);  }  /**   * Initialize the service, including the service object.   */  public void init(ServletConfig config)    throws ServletException  {    super.init(config);        try {      if (_homeImpl != null) {      }      else if (getInitParameter("home-class") != null) {String className = getInitParameter("home-class");Class homeClass = loadClass(className);_homeImpl = homeClass.newInstance();init(_homeImpl);      }      else if (getInitParameter("service-class") != null) {String className = getInitParameter("service-class");Class homeClass = loadClass(className);_homeImpl = homeClass.newInstance();init(_homeImpl);      }      else {if (getClass().equals(HessianServlet.class))  throw new ServletException("server must extend HessianServlet");_homeImpl = this;      }      if (_homeAPI != null) {      }      else if (getInitParameter("home-api") != null) {String className = getInitParameter("home-api");_homeAPI = loadClass(className);      }      else if (getInitParameter("api-class") != null) {String className = getInitParameter("api-class");_homeAPI = loadClass(className);      }      else if (_homeImpl != null) {_homeAPI = findRemoteAPI(_homeImpl.getClass());if (_homeAPI == null)  _homeAPI = _homeImpl.getClass();      }            if (_objectImpl != null) {      }      else if (getInitParameter("object-class") != null) {String className = getInitParameter("object-class");Class objectClass = loadClass(className);_objectImpl = objectClass.newInstance();init(_objectImpl);      }      if (_objectAPI != null) {      }      else if (getInitParameter("object-api") != null) {String className = getInitParameter("object-api");_objectAPI = loadClass(className);      }      else if (_objectImpl != null)_objectAPI = _objectImpl.getClass();      _homeSkeleton = new HessianSkeleton(_homeImpl, _homeAPI);      if (_objectAPI != null)_homeSkeleton.setObjectClass(_objectAPI);      if (_objectImpl != null) {_objectSkeleton = new HessianSkeleton(_objectImpl, _objectAPI);_objectSkeleton.setHomeClass(_homeAPI);      }      else_objectSkeleton = _homeSkeleton;    } catch (ServletException e) {      throw e;    } catch (Exception e) {      throw new ServletException(e);    }  }  private Class findRemoteAPI(Class implClass)  {    if (implClass == null || implClass.equals(GenericService.class))      return null;        Class []interfaces = implClass.getInterfaces();    if (interfaces.length == 1)      return interfaces[0];    return findRemoteAPI(implClass.getSuperclass());  }  private Class loadClass(String className)    throws ClassNotFoundException  {    ClassLoader loader = Thread.currentThread().getContextClassLoader();    if (loader != null)      return Class.forName(className, false, loader);    else      return Class.forName(className);  }  private void init(Object service)    throws ServletException  {    if (service instanceof Service)      ((Service) service).init(getServletConfig());    else if (service instanceof Servlet)      ((Servlet) service).init(getServletConfig());  }    /**   * Execute a request.  The path-info of the request selects the bean.   * Once the bean's selected, it will be applied.   */  public void service(ServletRequest request, ServletResponse response)    throws IOException, ServletException  {    HttpServletRequest req = (HttpServletRequest) request;    HttpServletResponse res = (HttpServletResponse) response;    if (! req.getMethod().equals("POST")) {      res.setStatus(500, "Hessian Requires POST");      PrintWriter out = res.getWriter();      res.setContentType("text/html");      out.println("<h1>Hessian Requires POST</h1>");            return;    }    String serviceId = req.getPathInfo();    String objectId = req.getParameter("id");    if (objectId == null)      objectId = req.getParameter("ejbid");    ServiceContext.begin(req, serviceId, objectId);    try {      InputStream is = request.getInputStream();      OutputStream os = response.getOutputStream();      Hessian2Input in = new Hessian2Input(is);      AbstractHessianOutput out;      SerializerFactory serializerFactory = getSerializerFactory();            in.setSerializerFactory(serializerFactory);      int code = in.read();      if (code != 'c') {// XXX: deflatethrow new IOException("expected 'c' in hessian input at " + code);      }      int major = in.read();      int minor = in.read();      if (major >= 2)out = new Hessian2Output(os);      elseout = new HessianOutput(os);            out.setSerializerFactory(serializerFactory);      if (objectId != null)_objectSkeleton.invoke(in, out);      else_homeSkeleton.invoke(in, out);      out.close();    } catch (RuntimeException e) {      throw e;    } catch (ServletException e) {      throw e;    } catch (Throwable e) {      throw new ServletException(e);    } finally {      ServiceContext.end();    }  }}



先看init()函数,功能还是一样,初始话一些东西,读入init-param的内容,并且load这些init-param的class

主要的还是service()函数
在service函数里边会获得request和response对象的输入和输出流,用来构造Hessian2Input和Hessian2Output,Hessian就是解析这两个东西来执行函数调用的。当然,在service里边还有一个重要的语句
Java代码
ServiceContext.begin(req, serviceId, objectId);

ServiceContext.begin(req, serviceId, objectId);
这个函数有点奇怪,我每次到这里serviceId和objectId都是空,不知道是不是历史遗留问题还存在这两个参数。
进去这个类看看

public class ServiceContext {     private static final ThreadLocal _localContext = new ThreadLocal();       private ServletRequest _request;     private String _serviceName;     private String _objectId;     private int _count;     private HashMap _headers = new HashMap();       private ServiceContext()     {     }          /**     * Sets the request object prior to calling the service's method.     *     * @param request the calling servlet request     * @param serviceId the service identifier     * @param objectId the object identifier     */    public static void begin(ServletRequest request,                  String serviceName,                  String objectId)       throws ServletException     {       ServiceContext context = (ServiceContext) _localContext.get();         if (context == null) {         context = new ServiceContext();         _localContext.set(context);       }         context._request = request;       context._serviceName = serviceName;       context._objectId = objectId;       context._count++;     }       /**     * Returns the service request.     */    public static ServiceContext getContext()     {       return (ServiceContext) _localContext.get();     }       /**     * Adds a header.     */    public void addHeader(String header, Object value)     {       _headers.put(header, value);     }       /**     * Gets a header.     */    public Object getHeader(String header)     {       return _headers.get(header);     }       /**     * Gets a header from the context.     */    public static Object getContextHeader(String header)     {       ServiceContext context = (ServiceContext) _localContext.get();         if (context != null)         return context.getHeader(header);       else        return null;     }       /**     * Returns the service request.     */    public static ServletRequest getContextRequest()     {       ServiceContext context = (ServiceContext) _localContext.get();         if (context != null)         return context._request;       else        return null;     }       /**     * Returns the service id, corresponding to the pathInfo of the URL.     */    public static String getContextServiceName()     {       ServiceContext context = (ServiceContext) _localContext.get();         if (context != null)         return context._serviceName;       else        return null;     }       /**     * Returns the object id, corresponding to the ?id= of the URL.     */    public static String getContextObjectId()     {       ServiceContext context = (ServiceContext) _localContext.get();         if (context != null)         return context._objectId;       else        return null;     }       /**     * Cleanup at the end of a request.     */    public static void end()     {       ServiceContext context = (ServiceContext) _localContext.get();         if (context != null && --context._count == 0) {         context._request = null;           context._headers.clear();       }     }       /**     * Returns the service request.     *     * @deprecated     */    public static ServletRequest getRequest()     {       ServiceContext context = (ServiceContext) _localContext.get();         if (context != null)         return context._request;       else        return null;     }       /**     * Returns the service id, corresponding to the pathInfo of the URL.     *     * @deprecated     */    public static String getServiceName()     {       ServiceContext context = (ServiceContext) _localContext.get();         if (context != null)         return context._serviceName;       else        return null;     }       /**     * Returns the object id, corresponding to the ?id= of the URL.     *     * @deprecated     */    public static String getObjectId()     {       ServiceContext context = (ServiceContext) _localContext.get();         if (context != null)         return context._objectId;       else        return null;     }   }  public class ServiceContext {  private static final ThreadLocal _localContext = new ThreadLocal();  private ServletRequest _request;  private String _serviceName;  private String _objectId;  private int _count;  private HashMap _headers = new HashMap();  private ServiceContext()  {  }    /**   * Sets the request object prior to calling the service's method.   *   * @param request the calling servlet request   * @param serviceId the service identifier   * @param objectId the object identifier   */  public static void begin(ServletRequest request,   String serviceName,   String objectId)    throws ServletException  {    ServiceContext context = (ServiceContext) _localContext.get();    if (context == null) {      context = new ServiceContext();      _localContext.set(context);    }    context._request = request;    context._serviceName = serviceName;    context._objectId = objectId;    context._count++;  }  /**   * Returns the service request.   */  public static ServiceContext getContext()  {    return (ServiceContext) _localContext.get();  }  /**   * Adds a header.   */  public void addHeader(String header, Object value)  {    _headers.put(header, value);  }  /**   * Gets a header.   */  public Object getHeader(String header)  {    return _headers.get(header);  }  /**   * Gets a header from the context.   */  public static Object getContextHeader(String header)  {    ServiceContext context = (ServiceContext) _localContext.get();    if (context != null)      return context.getHeader(header);    else      return null;  }  /**   * Returns the service request.   */  public static ServletRequest getContextRequest()  {    ServiceContext context = (ServiceContext) _localContext.get();    if (context != null)      return context._request;    else      return null;  }  /**   * Returns the service id, corresponding to the pathInfo of the URL.   */  public static String getContextServiceName()  {    ServiceContext context = (ServiceContext) _localContext.get();    if (context != null)      return context._serviceName;    else      return null;  }  /**   * Returns the object id, corresponding to the ?id= of the URL.   */  public static String getContextObjectId()  {    ServiceContext context = (ServiceContext) _localContext.get();    if (context != null)      return context._objectId;    else      return null;  }  /**   * Cleanup at the end of a request.   */  public static void end()  {    ServiceContext context = (ServiceContext) _localContext.get();    if (context != null && --context._count == 0) {      context._request = null;      context._headers.clear();    }  }  /**   * Returns the service request.   *   * @deprecated   */  public static ServletRequest getRequest()  {    ServiceContext context = (ServiceContext) _localContext.get();    if (context != null)      return context._request;    else      return null;  }  /**   * Returns the service id, corresponding to the pathInfo of the URL.   *   * @deprecated   */  public static String getServiceName()  {    ServiceContext context = (ServiceContext) _localContext.get();    if (context != null)      return context._serviceName;    else      return null;  }  /**   * Returns the object id, corresponding to the ?id= of the URL.   *   * @deprecated   */  public static String getObjectId()  {    ServiceContext context = (ServiceContext) _localContext.get();    if (context != null)      return context._objectId;    else      return null;  }}


原来ServiceContext 是用来保存当前调用线程的上下文的,比如request对象等(不知道这个解释对不对)。有了这个东西就太好了,因为里边有request,就有了调用端的一切信息,呵呵。

继续回来看那个Servlet,到了真正调用的时候了,也就是这段代码


if (objectId != null)   ctSkeleton.invoke(in, out);   else  Skeleton.invoke(in, out);        if (objectId != null)_objectSkeleton.invoke(in, out);      else_homeSkeleton.invoke(in, out);


跟踪invoke方法看看真面目


public void invoke(AbstractHessianInput in, AbstractHessianOutput out)     throws Throwable   {     ServiceContext context = ServiceContext.getContext();          String header;     while ((header = in.readHeader()) != null) {       Object value = in.readObject();         context.addHeader(header, value);     }     String ip = context.getContextRequest().getRemoteAddr();     String methodName = in.readMethod();     Method method = getMethod(methodName);       if (method != null) {     }     else if ("_hessian_getAttribute".equals(methodName)) {       String attrName = in.readString();       in.completeCall();         String value = null;         if ("java.api.class".equals(attrName))   alue = getAPIClassName();       else if ("java.home.class".equals(attrName))   alue = getHomeClassName();       else if ("java.object.class".equals(attrName))   alue = getObjectClassName();         out.startReply();         out.writeObject(value);         out.completeReply();       return;     }     else if (method == null) {       out.startReply();       out.writeFault("NoSuchMethodException",        "The service has no method named: " + in.getMethod(),        null);       out.completeReply();       return;     }       Class []args = method.getParameterTypes();     Object []values = new Object[args.length];          //args[0]       for (int i = 0; i < args.length; i++){       if(i == args.length-1){           values[i] = in.readObject(args[i], ip);       }else{           values[i] = in.readObject(args[i]);       }            }              in.completeCall();       Object result = null;          try {       result = method.invoke(_service, values);     } catch (Throwable e) {       if (e instanceof InvocationTargetException)         e = ((InvocationTargetException) e).getTargetException();         log.log(Level.WARNING, e.toString(), e);              out.startReply();       out.writeFault("ServiceException", e.getMessage(), e);       out.completeReply();       return;     }       out.startReply();       out.writeObject(result);          out.completeReply();   }    public void invoke(AbstractHessianInput in, AbstractHessianOutput out)    throws Throwable  {    ServiceContext context = ServiceContext.getContext();        String header;    while ((header = in.readHeader()) != null) {      Object value = in.readObject();      context.addHeader(header, value);    }    String ip = context.getContextRequest().getRemoteAddr();    String methodName = in.readMethod();    Method method = getMethod(methodName);    if (method != null) {    }    else if ("_hessian_getAttribute".equals(methodName)) {      String attrName = in.readString();      in.completeCall();      String value = null;      if ("java.api.class".equals(attrName))value = getAPIClassName();      else if ("java.home.class".equals(attrName))value = getHomeClassName();      else if ("java.object.class".equals(attrName))value = getObjectClassName();      out.startReply();      out.writeObject(value);      out.completeReply();      return;    }    else if (method == null) {      out.startReply();      out.writeFault("NoSuchMethodException",     "The service has no method named: " + in.getMethod(),     null);      out.completeReply();      return;    }    Class []args = method.getParameterTypes();    Object []values = new Object[args.length];        //args[0]    for (int i = 0; i < args.length; i++){    if(i == args.length-1){    values[i] = in.readObject(args[i], ip);    }else{    values[i] = in.readObject(args[i]);    }        }          in.completeCall();    Object result = null;        try {      result = method.invoke(_service, values);    } catch (Throwable e) {      if (e instanceof InvocationTargetException)        e = ((InvocationTargetException) e).getTargetException();      log.log(Level.WARNING, e.toString(), e);            out.startReply();      out.writeFault("ServiceException", e.getMessage(), e);      out.completeReply();      return;    }    out.startReply();    out.writeObject(result);        out.completeReply();  }


就是在这个方法里边,hessian把包装过的输入输出流当作参数传入并进行解析的,看看这个函数的第一句,正是取得ServiceContext的地方,此时应该就是把刚才Servlet里边保存的上下文取出来使用。
这个时候出现了第一个hack的地方 Java代码
String ip = context.getContextRequest().getRemoteAddr();

String ip = context.getContextRequest().getRemoteAddr();在此处我取得远程的ip地址保存起来。然后在第二个hack的地方

Class []args = method.getParameterTypes();   Object []values = new Object[args.length];     //args[0]     for (int i = 0; i < args.length; i++){       if(i == args.length-1){           values[i] = in.readObject(args[i], ip);       }else{           values[i] = in.readObject(args[i]);       }          }      Class []args = method.getParameterTypes();    Object []values = new Object[args.length];        //args[0]    for (int i = 0; i < args.length; i++){    if(i == args.length-1){    values[i] = in.readObject(args[i], ip);    }else{    values[i] = in.readObject(args[i]);    }        }
我用这个ip地址取代最后一个参数(web服务函数的参数,即远程端调用的函数的参数)。
第三个hack的地方就是 in.readObject(args[i], ip); 这个方法。 这个方法是我自己加的,原本只有
in.readObject(args[i]); 这个方法。 这个方法就是hessian读取参数值的地方
进去看看

/**   * Reads an object from the input stream with an expected type.   */  public Object readObject(Class cl, String ip)     throws IOException   {     if (cl == null || cl == Object.class)       return readObject();          int tag = _offset < _length ? (_buffer[_offset++] & 0xff) : read();       switch (tag) {     case 'N':       return null;       case 'M':     {       String type = readType();       Deserializer reader;       reader = findSerializerFactory().getObjectDeserializer(type);         if (cl != reader.getType() && cl.isAssignableFrom(reader.getType()))         return reader.readMap(this);         reader = findSerializerFactory().getDeserializer(cl);         return reader.readMap(this);     }       case 'O':     {       return readObjectDefinition(cl);     }       case 'o':     {       int ref = readInt();         ObjectDefinition def = (ObjectDefinition) _classDefs.get(ref - 1);         return readObjectInstance(cl, def);     }       case 'V':     {       String type = readType();       int length = readLength();              Deserializer reader;       reader = findSerializerFactory().getObjectDeserializer(type);              if (cl != reader.getType() && cl.isAssignableFrom(reader.getType()))         return reader.readList(this, length);         reader = findSerializerFactory().getDeserializer(cl);         Object v = reader.readList(this, length);         return v;     }       case 'v':     {       int ref = readInt();       String type = (String) _types.get(ref);       int length = readInt();              Deserializer reader;       reader = findSerializerFactory().getObjectDeserializer(type);              if (cl != reader.getType() && cl.isAssignableFrom(reader.getType()))         return reader.readLengthList(this, length);         reader = findSerializerFactory().getDeserializer(cl);         Object v = reader.readLengthList(this, length);         return v;     }       case 'R':     {       int ref = parseInt();         return _refs.get(ref);     }       case 'r':     {       String type = readType();       String url = readString();         return resolveRemote(type, url);     }     }       if (tag >= 0)       _offset--;       Object value = findSerializerFactory().getDeserializer(cl).readObject(this);       if(value instanceof String){       value = ip;     }     return value;   }    /**   * Reads an object from the input stream with an expected type.   */  public Object readObject(Class cl, String ip)    throws IOException  {    if (cl == null || cl == Object.class)      return readObject();        int tag = _offset < _length ? (_buffer[_offset++] & 0xff) : read();    switch (tag) {    case 'N':      return null;    case 'M':    {      String type = readType();      Deserializer reader;      reader = findSerializerFactory().getObjectDeserializer(type);      if (cl != reader.getType() && cl.isAssignableFrom(reader.getType()))        return reader.readMap(this);      reader = findSerializerFactory().getDeserializer(cl);      return reader.readMap(this);    }    case 'O':    {      return readObjectDefinition(cl);    }    case 'o':    {      int ref = readInt();      ObjectDefinition def = (ObjectDefinition) _classDefs.get(ref - 1);      return readObjectInstance(cl, def);    }    case 'V':    {      String type = readType();      int length = readLength();            Deserializer reader;      reader = findSerializerFactory().getObjectDeserializer(type);            if (cl != reader.getType() && cl.isAssignableFrom(reader.getType()))        return reader.readList(this, length);      reader = findSerializerFactory().getDeserializer(cl);      Object v = reader.readList(this, length);      return v;    }    case 'v':    {      int ref = readInt();      String type = (String) _types.get(ref);      int length = readInt();            Deserializer reader;      reader = findSerializerFactory().getObjectDeserializer(type);            if (cl != reader.getType() && cl.isAssignableFrom(reader.getType()))        return reader.readLengthList(this, length);      reader = findSerializerFactory().getDeserializer(cl);      Object v = reader.readLengthList(this, length);      return v;    }    case 'R':    {      int ref = parseInt();      return _refs.get(ref);    }    case 'r':    {      String type = readType();      String url = readString();      return resolveRemote(type, url);    }    }    if (tag >= 0)      _offset--;    Object value = findSerializerFactory().getDeserializer(cl).readObject(this);    if(value instanceof String){    value = ip;    }    return value;  }
我重载了这个方法,加入了一个String类型的参数,用来把ip地址传进去,并且最后返回这个值。到了这里,hack的原理大家应该知道了--就是强行修改远程调用端的调用函数里边的最后一个参数的值(规定为String类型),把这个值设为我想要的信息,那么服务端的服务函数就会获得这个值,并且进行后续处理。
剩下的步骤就原封不动的是hessian来处理了,没有需要干涉的地方,你也就能在你的服务端service函数里边获得这个你想要的信息了。

这就是Hessian的一个普通流程,不知道分析和Hack的对不对,我在这里是调试成功了,但是还没彻底测试有没有其它bug。 至于跟Spring的结合,待会儿跟帖来说。

读书人网 >软件架构设计

热点推荐