1. 程式人生 > 程式設計 >如何劫持Java應用的HTTP請求

如何劫持Java應用的HTTP請求

背景

全鏈路追蹤中,針對部分特殊的流量,希望將它引導到特定服務上(這個特定服務不在正常請求的鏈路上)——問題可以被抽象為解決程序間通訊過程中目標程序的選擇。

程序間通訊方式很多,本篇只關注 Java 程序間套接字通訊下 HTTP 形式的請求劫持,引導特定流量到特定程序。

解決方案

可行的處理方案繁多。自頂向下從應用、框架、JVM、Container Runtime、System Call、網路協議棧等級別,均可著手解決。侵入性最強的操作就是要求所有業務應用都主動實現 HTTP 請求分流邏輯;次一級是提供二方庫供業務應用主動整合;或者從系統層面進行改造,基於改寫系統呼叫對請求進行劫持。

回顧兩年前的所學,JVM TI 為劫持 HTTP 請求提供了一個全新的解決思路。通過 Agent 改寫應用啟動時載入的類的位元組碼,劫持類的例項並完成目標功能。

由於 Java 專案間呼叫大量的使用了 Apache 的 http-client 庫,改寫變得相當簡單。識別流量,並對特定流量改寫請求的 Host 即可。

Demo

由於 http-client 對所有請求目標都統一由 org.apache.http.HttpHost 維護,控制變得極為簡單。只需在 HttpHost 例項化時,改寫類的構造方法,即完成了對目標的劫持工作(下例中強制將所有請求指向 163.com

public class Agent implements ClassFileTransformer {

  public static void premain(String args,Instrumentation instrumentation) {
    instrumentation.addTransformer(new Agent());
  }

  @Override
  public byte[] transform(ClassLoader loader,String className,Class<?> classBeingRedefined,ProtectionDomain protectionDomain,byte[] classfileBuffer) throws IllegalClassFormatException {
    if ("org/apache/http/HttpHost".equals(className)) {
      ClassPool pool = ClassPool.getDefault();
      try {
        CtClass httpHost = pool.get("org.apache.http.HttpHost");
        CtClass string = pool.get("java.lang.String");
        CtConstructor constructor = httpHost.getDeclaredConstructor(new CtClass[]{string,CtClass.intType,string});
        constructor.insertBefore("hostname = \"www.163.com\";");
        byte[] bytes = httpHost.toBytecode();
        FileOutputStream fos = new FileOutputStream("/Users/fangfeng/a.class");
        fos.write(bytes);
        return bytes;
      } catch(NotFoundException | CannotCompileException | IOException e){
        e.printStackTrace();
      }
    }
    return classfileBuffer;
  }
}

將整個專案打包為 agent.jar 的過程不做太多介紹,詳見 ffutop/http-client-plugin

針對需要劫持的專案,在啟動引數中增加 -javaagent:${PATH_TO}/http-client-plugin.jar

以上就是如何劫持Java應用的HTTP請求的詳細內容,更多關於劫持Java應用的HTTP請求的資料請關注我們其它相關文章!