我有一个独立的Java客户端,试图通过NTLM代理执行RMI。
它是多线程的。
我正在使用Apache httpclient 4.5.6。
我有一个5分钟的超时周期的代理。
基本情况是可行的,只要受到代理的挑战,每5分钟就会重新进行身份验证,只要两个线程在代理超时的确切时间未同时发出请求即可。然后失败。一旦失败,所有后续尝试都会失败。
我已经附上了wireshark屏幕截图以进行澄清(屏幕截图来自4.5.2,但我升级到4.5.6并看到了相同的行为)。
一个好的周期看起来像
客户端尝试连接(无NTML标志)
代理回复407(无NTML标志)
客户端再次尝试使用ntlm消息类型NTLMSSP_NEGOTIATE进行CONNECT
代理回复407 NTLMSSP_CHALLENGE
客户端确实使用NTLMSSP_AUTH和我的凭据进行连接。
代理回复200,我们很高兴再等5分钟。
糟糕的周期看起来像
客户端尝试连接(无NTML标志)
代理回复407(无NTML标志)
客户端再次尝试使用ntlm消息类型NTLMSSP_NEGOTIATE进行CONNECT
客户端尝试连接(无NTML标志)
代理回复407(无NTML标志)
代理回复407 NTLMSSP_CHALLENGE
几秒钟之内,便有了更多的不带NTML标志的CONNECT和407。
对我来说,这看起来像非线程安全代码中的多线程竞争条件。
使用Apache httpclient 4.5.2,它只是传播了407,我在CloseableHttpResponse.getStatusLine()。getStatusCode()中检测到它。
使用Apache httpclient 4.5.6,我看到了
java.lang.IllegalStateException: Auth scheme is null
at org.apache.http.util.Asserts.notNull(Asserts.java:52)
at org.apache.http.impl.auth.HttpAuthenticator.ensureAuthScheme(HttpAuthenticator.java:229)
at org.apache.http.impl.auth.HttpAuthenticator.generateAuthResponse(HttpAuthenticator.java:184)
at org.apache.http.impl.execchain.MainClientExec.createTunnelToTarget(MainClientExec.java:484)
at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:411)
at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:237)
at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:185)
at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89)
at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110)
at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83)
有什么想法可以防止这种情况发生或如何解决或从中恢复吗?
(除了同步通话,这会大大降低已经很慢的应用的运行速度)
该应用程序的一些代码段:
// this is done only once
HttpClientBuilder builder = HttpClients.custom();
SocketConfig.Builder socketConfig = SocketConfig.custom();
RequestConfig.Builder requestConfig = RequestConfig.custom();
HttpHost proxy = new HttpHost(proxyHost, proxyPort);
builder.setProxy(proxy);
requestConfig.setProxy(proxy);
builder.setProxyAuthenticationStrategy(new ProxyAuthenticationStrategy());
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
String localHost = getLocalHostname();
credentialsProvider.setCredentials(
new AuthScope(proxyHost, proxyPort, AuthScope.ANY_REALM, "ntlm"),
new NTCredentials(user, password, localHost, domain));
builder.setDefaultCredentialsProvider(credentialsProvider);
builder.setDefaultSocketConfig(socketConfig.build());
builder.setDefaultRequestConfig(requestConfig.build());
CloseableHttpClient client = builder.build();
...
// cached, we use the same one every time in accordance with section 4.7 of
// https://hc.apache.org/httpcomponents-client-4.5.x/tutorial/html/authentication.html
HttpClientContext context = HttpClientContext.create();
context.setCredentialsProvider(credentialsProvider);
...
// new HttpPost every time
HttpPost postMethod = new HttpPost(uri);
postMethod.setEntity(new ByteArrayEntity(bytesOut.toByteArray()));
response = client.execute(postMethod, context);
最佳答案
HttpContext
实例是完全线程安全的。但是,存储在上下文中的某些属性(如身份验证握手状态)显然不是。确保HttpContext
实例不会同时更新,并且该问题应消除。
关于java - NTLM和Java Apache httpclient 4.5.6的多线程问题,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/51851162/