首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Hessian 源碼深度分析與 Hack 實戰:讓遠端呼叫攜帶客戶端資訊

     編輯:Java 相關知識

背景:輕量級 Hessian 的資訊缺口

專案採用 Hessian 作為 Web Service 實作,二進制協議效能逼近 Socket。但業務需求需獲取遠端呼叫端 IP、主機名等資訊。官方文檔、論壇、郵件列表均無解,決定深入源碼尋找突破口。

純 Hessian 呼叫流程剖析

入口:HessianServlet

`web.xml` 配置 `HessianServlet`(程式碼區塊 1),初始化參數指定服務介面與實作類。

核心初始化

`init(ServletConfig)` 讀取參數、載入類別、建立 `HessianSkeleton`(程式碼區塊 2 節錄)。關鍵行:

ServiceContext.begin(req, serviceId, objectId);

ServiceContext:線程級上下文持有者

使用 `ThreadLocal` 保存當前請求的 `ServletRequest`、`serviceName`、`objectId` 等(程式碼區塊 3)。提供靜態方法:

  • `getContextRequest()` → 取得 `HttpServletRequest`
  • `getContextServiceName()` / `getContextObjectId()`

這意味著**服務端代碼可隨時透過 `ServiceContext.getContextRequest()` 獲取完整請求物件**,包含客戶端 IP、Header 等所有資訊。

真正的呼叫處理:Skeleton.invoke()

解析 Hessian 輸入流、讀取 Header、方法名、參數,反射呼叫服務方法,寫入回覆(程式碼區塊 4)。關鍵發現:

String ip = context.getContextRequest().getRemoteAddr();

此處已可取得客戶端 IP!但 Hessian 預設不將其傳遞給業務方法。

Hack 策略:強制注入 IP 至最後一個參數

三處修改點

  1. 取得 IP:`invoke()` 開頭 `String ip = context.getContextRequest().getRemoteAddr();`
  2. 參數陣列植入:讀取參數迴圈中,最後一個參數強制替換:
    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]);
    }
  3. 擴充反序列化:在 `AbstractHessianInput` 新增 `readObject(Class cl, String ip)` 多載(程式碼區塊 5),邏輯同原方法,但若反序列化結果為 `String` 則直接回傳注入的 `ip`。

運作原理

客戶端呼叫 `serviceMethod(param1, param2, String clientIp)`,最後一個參數雖由客戶端傳送,但服務端反序列化時被攔截替換為真實 IP。**服務端業務代碼零修改**,自然獲得客戶端資訊。

限制與風險提示

  • **約定大於配置**:要求服務方法最後一個參數為 `String` 類型,且語意為「接收客戶端 IP」
  • **破壞協議語意**:客戶端傳送的實際值被丟棄,可能導致邏輯誤解
  • **源碼維護成本**:修改 Hessian 核心類,升級時需重新移植 Hack
  • **未充分測試**:僅驗證基本流程,併發、異常、不同資料類型場景未覆蓋

Spring 整合場景展望

Spring 的 `HessianServiceExporter` / `HessianProxyFactoryBean` 同樣委派 `HessianServlet`/`HessianSkeleton`,Hack 原理通用。後續將分享 Spring 整合細節與替代方案(如 Filter/Interceptor 注入 ThreadLocal、自定義序列化策略等)。

結語

Hessian 設計簡潔,`ServiceContext` 線程上下文機制為 Hack 提供天然切入點。雖以「暴力」方式解決資訊傳遞,但揭示了框架內部運作細節。生產環境建議評估:是否可改用標準 Header 傳遞?是否接受維護 Fork 版本? 歡迎社群提出更優雅的解法。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved