2009-09-18 43 views

回答

10

我已經能夠登錄默認的基礎上實現我自己的SSLSocketFactory所有SSL流量。

這爲我工作,因爲我們所有的連接都使用HTTPS,我們可以用該方法HttpsURLConnection.setSSLSocketFactory設置套接字工廠。

更完整的解決方案,使所有的插座監控可以在http://www.javaspecialists.eu/archive/Issue169.html 由於被發現Lawrence Dol在使用Socket.setSocketImplFactory

這是我沒有準備好生產代碼的指向正確的方向:

public class WireLogSSLSocketFactory extends SSLSocketFactory { 

    private SSLSocketFactory delegate; 

    public WireLogSSLSocketFactory(SSLSocketFactory sf0) { 
     this.delegate = sf0; 
    } 

    public Socket createSocket(Socket s, String host, int port, 
      boolean autoClose) throws IOException { 
     return new WireLogSocket((SSLSocket) delegate.createSocket(s, host, port, autoClose)); 
    } 

    /* 
    ... 
    */ 

    private static class WireLogSocket extends SSLSocket { 

     private SSLSocket delegate; 

     public WireLogSocket(SSLSocket s) { 
      this.delegate = s; 
     } 

     public OutputStream getOutputStream() throws IOException { 
      return new LoggingOutputStream(delegate.getOutputStream()); 
     } 

     /* 
     ... 
     */ 

     private static class LoggingOutputStream extends FilterOutputStream { 
      private static final Logger logger = Logger.getLogger(WireLogSocket.LoggingOutputStream.class); 
      //I'm using a fixed charset because my app always uses the same. 
      private static final String CHARSET = "ISO-8859-1"; 
      private StringBuffer sb = new StringBuffer(); 

      public LoggingOutputStream(OutputStream out) { 
       super(out); 
      } 

      public void write(byte[] b, int off, int len) 
        throws IOException { 
       sb.append(new String(b, off, len, CHARSET)); 
       logger.info("\n" + sb.toString()); 
       out.write(b, off, len); 
      } 

      public void write(int b) throws IOException { 
       sb.append(b); 
       logger.info("\n" + sb.toString()); 
       out.write(b); 
      } 

      public void close() throws IOException { 
       logger.info("\n" + sb.toString()); 
       super.close(); 
      } 
     } 
    } 
} 
+1

Socket.setSocketImplFactory(),對於非SSL,可能? – 2010-02-23 04:06:15

+1

@Serhill:一些詳細的例子和/或代碼摘錄可能會讓你接受。 – 2010-02-23 04:07:24

+0

我認爲javaspecialist的「SSL流量實現我自己的SSLSocketFactory的默認值」解決方案是最好的。 – 2010-02-24 02:27:43

2

我不認爲你可以自動做到這一點,但你可以將FilterOutputStreamFilterInputStream的子集與HttpUrlConnection的輸出和輸入流作爲參數一起使用。然後,當字節被寫入/讀取時,記錄它們並將它們傳遞給底層流。

+0

我能捕捉到的HTTP標頭用這個解決方案?根據我的理解您的提議,只有帖子或獲取內容會通過流。 – Serxipc 2009-09-19 20:01:40

+0

@Serhii這將包括進出的所有東西,所以標題包括在內。 – Bozho 2010-02-21 07:48:34

+2

@Bozho:其實我不認爲它會包含標題。在獲取流時,頭文件已經發送(用於輸出流)或者接收並解析(用於輸入流)。 – 2010-02-23 04:00:49

7

解決方案#1:使用裝飾模式

你將不得不使用Decorator pattern上的HttpURLConnection類來擴展它的功能。然後覆蓋所有HttpURLConnection方法並將操作委託給組件指針,並捕獲所需的信息並記錄下來。

另外,還要確保你覆蓋父類URLConnection.getOutputStream()OutputStreamURLConnection.html#getInputStream()InputStream方法返回裝飾OutputStreamInputStream對象爲好。

解決方案2:使用自定義的內存中HTTP代理

寫一個簡單的HTTP代理服務器,並將它開始在它的在應用程序啓動和初始化單獨的線程。見Example simple proxy server

讓你的應用程序配置爲使用上述的所有請求的HTTP代理。見configuring Java to use Proxies

現在所有的流量正在經歷上面的代理,就像它在小提琴手是如何發生的。因此,您可以訪問原始http流「從客戶端到服務器」以及「從服務器返回客戶端」。您必須解釋這些原始信息並根據需要進行記錄。

更新: 使用HTTP代理作爲基於SSL的Web服務器的適配器。

== Client System =============================== 
    |            | 
    | ------------------------------   | 
    | |        |   | 
    | | Java process    |   | 
    | |      ---- |   | 
    | |  ---------- | | |   | 
    | |  |   | -O | |   | 
    | |  | Logging |  | |   | 
    | |  | Proxy <---HTTP-- | ----- | 
    | |  | Adapter |   | |  | | 
    | |  | Thread o------------------> | | 
    | |  |  o |   | |  | | 
    | |  --------|-   | | Log | | 
    | |    |    | ----- | 
    | ----------------|-------------   | 
    |     |       | 
    =====================|========================== 
         |       
         |       
        HTTPS       
         SSL       
         |       
    == Server System ====|========================== 
    |     |       | 
    | ----------------|----------------   | 
    | |    V    |  | 
    | |         |  | 
    | | Web Server     |  | 
    | |         |  | 
    | ---------------------------------   | 
    |            | 
    ================================================ 
+0

查看JDK6源頭是在私有方法sun.net.www.protocol.http.HttpURLConnection.writeRequests中發送的。使用修飾的HttpURLConnection,我將能夠記錄方法調用和流內容,但是我將無法訪問負責創建套接字並寫入它的底層sun.net.HttpClient實例。 – Serxipc 2010-02-19 12:12:07

+0

增加解決方案#2:使用自定義的內存http代理 – 2010-02-19 16:31:53

+0

自定義代理無法處理ssl。這就是我正在尋找直接從java中記錄流量而不是使用fiddler的原因。 – Serxipc 2010-02-22 18:33:57

2

怎麼樣使用AspectJ插入一個切入點來繞方法添加日誌記錄的建議?我相信AspectJ可以編入私有/受保護的方法。

看來,sun.net.www.protocol.http.HttpURLConnection.writeRequest可能會調用sun.net.www.http.HttpClient.writeRequest,它將MessageHeader對象作爲輸入,這將成爲您的目標。

最終這可能會工作,但會非常脆弱,只能在Sun JVM上運行;真的你只能相信你使用的確切版本。

+3

取決於未發佈的,未公開的私有API是**糟糕的主意。 – 2010-02-23 04:01:47

+0

@勞倫斯代爾:確實。但是,對於單個調試會話,這可能是好的。 ' – sleske 2017-07-12 11:54:27

2

如果你只想獲得線上的內容(頭文件,正文等),那麼你可能想要給wireshark一個提示。

這具有無需更改任何代碼的優點,但如果啓用通過代碼進行日誌記錄是您所追求的內容,則此答案不適用。

32

根據Sun's HttpURLConnection source有一些日誌支持通過JUL

設置(根據需要進行調整的路徑):

-Djava.util.logging.config.file=/full/path/to/logging.properties 

logging.properties:

handlers= java.util.logging.ConsoleHandler 
java.util.logging.ConsoleHandler.level = FINEST 
sun.net.www.protocol.http.HttpURLConnection.level=ALL 

這將記錄到控制檯,按要求例如調整登錄到文件。

輸出示例:

2010-08-07 00:00:31 sun.net.www.protocol.http.HttpURLConnection writeRequests 
FIN: [email protected] pairs: {GET /howto.html HTTP/1.1: null}{User-Agent: Java/1.6.0_20}{Host: www.rgagnon.com}{Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2}{Connection: keep-alive} 
2010-08-07 00:00:31 sun.net.www.protocol.http.HttpURLConnection getInputStream 
FIN: [email protected] pairs: {null: HTTP/1.1 200 OK}{Date: Sat, 07 Aug 2010 04:00:33 GMT}{Server: Apache}{Accept-Ranges: bytes}{Content-Length: 17912}{Keep-Alive: timeout=5, max=64}{Connection: Keep-Alive}{Content-Type: text/html} 

注意,這僅打印頭而不體。

查看http://www.rgagnon.com/javadetails/java-debug-HttpURLConnection-problem.html瞭解詳情。

還有系統屬性-Djavax.net.debug=all。但它主要是useful for SSL debugging

+8

程序可以使用 'sun.util.logging.PlatformLogger .getLogger( 「sun.net.www.protocol.http.HttpURLConnection」) .setLevel(PlatformLogger.Level.ALL)做到這一點引用應該已經存在的文件「JRE_HOME \ lib \ logging.properties」。如果沒有看到任何輸出,日誌記錄級別可能太低,找到'java.util.logging.ConsoleHandler.level'行並將其設置爲'FINEST'。 – 2016-02-12 22:38:04

+1

這將; – user3792852 2016-08-30 08:51:26

+1

@ user3792852:好點。我冒昧地將其添加到答案中,希望你不介意。 – sleske 2017-07-12 12:00:00

2

在Linux中,你可以使用strace下運行VM:

strace的-o strace.out -s -e 4096一絲=網絡-f java的...