我有一个需要访问 Web 服务的 Java 应用程序(不是小程序)。 Web 服务的代理已使用 JAX-WS 生成,并且似乎工作正常。在一种情况下,它需要通过 Web 代理服务器(实际上是 Squid 3.0)进行对话,该服务器被设置为需要 NTLM 身份验证。
在 Sun 的 JRE 1.6.0_14 上运行,访问 HTTP URL 一切正常,无需任何更改:内置的 NTLM 身份 validator 启动,一切都无缝运行。但是,如果 Web 服务 URL 是 HTTPS URL,则 Web 服务调用将在 Sun 的代码深处失败:
com.sun.xml.internal.ws.client.ClientTransportException: HTTP transport error: java.lang.NullPointerException
at com.sun.xml.internal.ws.transport.http.client.HttpClientTransport.getOutput(HttpClientTransport.java:121)
at com.sun.xml.internal.ws.transport.http.client.HttpTransportPipe.process(HttpTransportPipe.java:142)
at com.sun.xml.internal.ws.transport.http.client.HttpTransportPipe.processRequest(HttpTransportPipe.java:83)
at com.sun.xml.internal.ws.transport.DeferredTransportPipe.processRequest(DeferredTransportPipe.java:105)
at com.sun.xml.internal.ws.api.pipe.Fiber.__doRun(Fiber.java:587)
at com.sun.xml.internal.ws.api.pipe.Fiber._doRun(Fiber.java:546)
at com.sun.xml.internal.ws.api.pipe.Fiber.doRun(Fiber.java:531)
at com.sun.xml.internal.ws.api.pipe.Fiber.runSync(Fiber.java:428)
at com.sun.xml.internal.ws.client.Stub.process(Stub.java:211)
at com.sun.xml.internal.ws.client.sei.SEIStub.doProcess(SEIStub.java:124)
at com.sun.xml.internal.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:98)
at com.sun.xml.internal.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:78)
at com.sun.xml.internal.ws.client.sei.SEIStub.invoke(SEIStub.java:107)
... our web service call ...
Caused by: java.lang.NullPointerException
at sun.net.www.protocol.http.NTLMAuthentication.setHeaders(NTLMAuthentication.java:175)
at sun.net.www.protocol.http.HttpURLConnection.doTunneling(HttpURLConnection.java:1487)
at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:164)
at sun.net.www.protocol.http.HttpURLConnection.getOutputStream(HttpURLConnection.java:896)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:230)
at com.sun.xml.internal.ws.transport.http.client.HttpClientTransport.getOutput(HttpClientTransport.java:109)
... 16 more
查看 Sun 的错误数据库会发现此类类中存在一些异常,但它们似乎都已修复。有没有人遇到过这样的事情?有人让这个工作吗?
最佳答案
经过一些调试,这似乎是 JRE 类库中的一个缺陷,特别是在 sun.net.www.protocol.http.HttpURLConnection
中。
研究 HTTP 和 HTTPS 端点情况下的 HTTP 请求和响应表明,在成功的 HTTP 情况下,请求有一个 header Proxy-Connection=keep-alive
,它在失败的 HTTPS 案例。更一般地阅读,对于应该使用“Proxy-Connection”还是只使用“Connection”似乎存在一些困惑......
无论如何,值得注意的是,在 HTTP 情况下,代码通过 HttpURLConnection.writeRequests()
,其中包含以下代码片段
/*
* For HTTP/1.1 the default behavior is to keep connections alive.
* However, we may be talking to a 1.0 server so we should set
* keep-alive just in case, except if we have encountered an error
* or if keep alive is disabled via a system property
*/
// Try keep-alive only on first attempt
if (!failedOnce && http.getHttpKeepAliveSet()) {
if (http.usingProxy) {
requests.setIfNotSet("Proxy-Connection", "keep-alive");
} else {
requests.setIfNotSet("Connection", "keep-alive");
}
在通过 HTTPS 代理创建隧道时没有这样的代码,这会导致 squid 在 NTLM 身份验证对话期间感到不安。
为了解决这个问题,我在 HttpURLConnection.sendCONNECTRequest()
中添加了
if (http.getHttpKeepAliveSet()) {
if (http.usingProxy) {
requests.setIfNotSet("Proxy-Connection", "keep-alive");
}
}
就在
setPreemptiveProxyAuthentication(requests);
http.writeRequests(requests, null);
我使用“-Xbootclasspath/p”标志将修改后的 HttpURLConnection.class
注入(inject)到 JRE 中,现在它可以工作了!不完全优雅,但我们就是这样。
关于Java 6 NTLM 代理身份验证和 HTTPS - 有人让它工作吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1326849/