记录Java应用程序的所有网络交互

30

我有一个庞大的Java应用程序(GNUEnterprise这个较为不知名的应用服务器的客户端),我有它的源代码,可以在进行一些更改后再次编译。该应用程序使用网络非常频繁,我需要监视每个请求和响应。虽然我可以使用类似Wireshark这样的嗅探器,但是该应用程序与其服务器之间通过SSL进行通信,因此如果不知道SSL证书的私钥,则任何被嗅探的流量都是无用的。

我该如何做才能使应用程序自身记录每个请求和响应?我需要看到所有发送和接收的头信息。我不想更改负责网络交互的所有代码。我的目标是像下面这样插入一些代码:

Network.setDefaultLogger(myCustomLoggerInstance);

在应用程序开始的地方附近,然后在myCustomLoggerInstance中执行所需的所有日志记录。

另外,由于所有网络操作都是使用URLConnection进行的,因此可以使用con.getHeaderFields()获取响应标头,并使用con.getRequestProperties()获取请求标头。但为什么没有Cookie?如何以相同的方式转储发送和接收的Cookie?

编辑:我想达到的目标是模仿RPC应用程序与其服务器之间的通信(例如,使用curl)。为此,我需要获取应用程序的网络流量的详细日志记录。


我会修改 OpenJDK,实现所提到的日志记录功能,然后在库级别上嗅探通信。 - auselen
@auselen 太过hacky了。如果在应用程序级别没有办法实现(似乎确实如此),我还是会坚持使用外部日志代理服务器的方式,就像greyfairer建议的那样。 - Hnatt
5个回答

34

一个快速记录所有SSL流量的方法是使用以下java启动命令:

-Djavax.net.debug=all

或将其设置为系统属性:

System.setProperty("javax.net.debug","all");

做好准备。如果你这样做,标准输出会变得很嘈杂!

(*注意:仅记录SSL流量和SSL调试信息(握手等)。常规套接字不会被记录。)


22

你不能使用一个日志代理服务器,例如http://www.charlesproxy.com/http://fiddler2.com/吗? Charles Proxy也可以解码AMF请求,而Fiddler是SSL拦截的冠军。

如果我没记错,你可以将系统属性http.proxyHost和http.proxyPort设置为127.0.0.1和8888,java URLConnections将自动通过你的日志代理服务器连接,你可以获得一个漂亮的HTTP流量视图。

或者,如果意图是能够重放通信,请使用JMeter HTTP Proxy,这将记录适合重放的格式。它还具有处理cookie的内置支持。

如果应用程序使用不错的Web服务,SoapUI还具有监控代理,可拦截已导入WSDL的Web服务调用。


哦,而且它还具有SSL流量嗅探功能。我一定会试试这个。 - Hnatt
要允许SSL嗅探,您还需要黑客破解您的Java信任存储,请参见:https://dev59.com/Emoy5IYBdhLWcg3wcNrh - GeertPt
我正在开发的应用程序已经处理了这个问题。它有一个类,利用 HttpsURLConnection.setDefaultHostnameVerifier.setDefaultSSLSocketFactory 来信任所有证书。如果没有应用程序级别的解决方案建议,我将接受您的答案,因为它实际上做到了我想要的,尽管方式不同。我还发现了一个名为 webscarab 的免费替代品,它在 Linux 上运行,不像 Fiddler。 - Hnatt
我坚持使用Webscarab代理。感谢您指引我正确的方向! - Hnatt
2
你叫我搜索“ IIRC”,这意味着“如果我记得正确的话”。 - Jalal Sordo

5

1
无法使其正常工作。现在我也更喜欢使用代理服务器。 - Hnatt

2
我想提供一段代码片段作为起点。虽然我没有一个“全局”记录器,但通过对现有代码进行小修改,您可以达到您的目标。我在标准输出上“记录”,因为您的问题可以分为两个部分:从UrlConnection获取信息并将其存储以供进一步参考。
此代码记录请求:
public class URLConnectionReader {

  public static void main(String[] args) throws Exception {
  URL oracle = new URL("http://www.google.com/");
  URLConnection yc = oracle.openConnection();

  String headerName = null;
  for (int i = 1; (headerName = yc.getHeaderFieldKey(i)) != null; i++) {
    if (headerName.equals("Set-Cookie")) {
      String cookie = yc.getHeaderField(i);
      System.out.println("Cookie  ::  " + cookie);
    } else {
      System.out.println("Header: "+ yc.getHeaderField(i));
    }
  }

  BufferedReader in = new BufferedReader(new InputStreamReader(yc.getInputStream()));
  String inputLine;
  while ((inputLine = in.readLine()) != null)
    System.out.println(inputLine);
  in.close();
}

将所有这些数据保存到一个或多个文件中并不难。如果您认为我对您的问题有帮助,我很乐意根据需要编辑答案并提供其他详细信息。


谢谢,但看起来 getHeaderFieldKey(i)getHeaders() 由于某种原因跳过了 Set-Cookie 头。无论如何,我正在寻找一种在整个应用程序中设置全局网络日志记录器的方法,而不必在每次网络交互处放置代码片段。另外,使用你的解决方案,我必须读取连接两次。 - Hnatt
当然,将日志保存到文件中并不是问题。因此,我将把我的问题分成两个部分:1)设置全局记录器;2)在不读取或发送响应和请求两次的情况下记录流量。 - Hnatt
糟糕,我的错。如果在 URLConnection 实例上发送请求后调用 getHeaders(),它不会第二次打开读取流,实际上您可以在那里看到 Set-Cookie 标头。所以对您来说是加1,对于所有错误的声明我很抱歉。剩下的问题是如何为所有网络连接设置代理(我的意思是 OOP 模式 Proxy,而不是代理服务器)。 - Hnatt
为什么不简单地扩展URLConnection并覆盖getConnection?在构造函数中放入您的记录器实例。只是一个猜测。 - giampaolo
@tapo:我正在寻找类似的东西,但如果我扩展URLConnection,我需要用我的CustomURLConnection替换每个URLConnection构造函数。如果我确定URLConnection总是通过构造函数实例化,那么这不会是一个大问题。但是可能有返回URLConnection实例的方法,比如URL.openConnection()。所以我还需要扩展和替换URL,也许还有其他一些东西。我不想在程序代码中乱搞太多,特别是到处插入片段。 - Hnatt

1
如果您想记录网络流量并且拥有URLConnection对象,则问题已经解决!如果您想在流级别上记录,您需要在I/O流之上编写一个小包装器,并记录所有数据,然后将它们传输到较低的网络层,否则您可以直接使用连接对象获取所需信息并记录它们。对于管理cookie,您应该使用Java 6中引入的java.net.HttpCookie。您唯一需要做的是创建CookieManager实例并将其设置为默认cookie管理器:
CookieManager cookieManager = new CookieManager();
CookieHandler.setDefault(cookieManager);

&你可以使用它来管理连接的cookies!


最初我在寻找类似于全局cookie管理器对象的东西,只是针对所有网络操作,但后来我决定使用本地日志代理服务器就足够了。在这种情况下,我甚至不需要干预源代码。顺便说一句,人们可以使用自定义的CookieManager来记录传入和传出的请求头,而不仅仅是cookie。CookieManager的方法getput分别具有类型为List<String>的第二个参数requestHeadersresponseHeaders。我只想知道这些方法是否在每个请求和响应时都被调用,还是仅在使用cookie时才调用。 - Hnatt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接