我正在尝试使用Apache / Jakarta HttpClient 4.1.1使用给定的凭据连接到任意网页。为了测试这一点,我在运行的dev机器上安装了最少的IIS 7.5,其中一次只有一种身份验证模式处于活动状态。基本身份验证工作正常,但每当我尝试登录时,Digest和NTLM都会返回401错误消息。这是我的代码:
DefaultHttpClient httpclient = new DefaultHttpClient();
HttpContext localContext = new BasicHttpContext();
HttpGet httpget = new HttpGet("http://localhost/");
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(AuthScope.ANY,
new NTCredentials("user", "password", "", "localhost"));
if (!new File(System.getenv("windir") + "\\krb5.ini").exists()) {
List<String> authtypes = new ArrayList<String>();
authtypes.add(AuthPolicy.NTLM);
authtypes.add(AuthPolicy.DIGEST);
authtypes.add(AuthPolicy.BASIC);
httpclient.getParams().setParameter(AuthPNames.PROXY_AUTH_PREF,
authtypes);
httpclient.getParams().setParameter(AuthPNames.TARGET_AUTH_PREF,
authtypes);
}
localContext.setAttribute(ClientContext.CREDS_PROVIDER, credsProvider);
HttpResponse response = httpclient.execute(httpget, localContext);
System.out.println("Response code: " + response.getStatusLine());
我在Fiddler中注意到的一件事是Firefox和HttpClient发送的哈希值不同,让我觉得IIS 7.5可能比HttpClient提供更强的哈希值?有任何想法吗?如果我能够验证这适用于NTLM,那就太棒了。摘要也会很好,但如果有必要,我可以不用它。
答案 0 :(得分:9)
我不是这方面的专家,但在使用http组件的NTLM身份验证期间,我看到客户端需要3次尝试才能连接到我的情况下的NTML端点。对于Spnego来说有点here,但对于NTLM身份验证它有点不同。
对于第一次尝试的NTLM,客户端将使用Target auth state: UNCHALLENGED
发出请求,Web服务器返回HTTP 401状态和标题:WWW-Authenticate: NTLM
客户端将检查配置的身份验证方案,应在客户端代码中配置NTLM。
第二次尝试,客户端将使用Target auth state: CHALLENGED
发出请求,并将发送一个带有以base64格式编码的令牌的授权标头:Authorization: NTLM TlRMTVNTUAABAAAAAYIIogAAAAAoAAAAAAAAACgAAAAFASgKAAAADw==
服务器再次返回HTTP 401状态,但标题:WWW-Authenticate: NTLM
现在填充了编码信息。
第三次尝试客户端将使用WWW-Authenticate: NTLM
标头中的信息,并将使用Target auth state: HANDSHAKE
和授权标头Authorization: NTLM
发出最终请求,其中包含服务器的更多信息。
就我而言,之后我会收到HTTP/1.1 200 OK
。
为了避免在第4.7.1节中的每个请求documentation中的所有这一切,请说明必须将相同的执行令牌用于逻辑相关的请求。对我而言,它没有用。
我的代码:
我在EJB的@PostConstruct
方法中初始化客户端
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
cm.setMaxTotal(18);
cm.setDefaultMaxPerRoute(6);
RequestConfig requestConfig = RequestConfig.custom()
.setSocketTimeout(30000)
.setConnectTimeout(30000)
.setTargetPreferredAuthSchemes(Arrays.asList(AuthSchemes.NTLM))
.setProxyPreferredAuthSchemes(Arrays.asList(AuthSchemes.BASIC))
.build();
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY,
new NTCredentials(userName, password, hostName, domainName));
// Finally we instantiate the client. Client is a thread safe object and can be used by several threads at the same time.
// Client can be used for several request. The life span of the client must be equal to the life span of this EJB.
this.httpclient = HttpClients.custom()
.setConnectionManager(cm)
.setDefaultCredentialsProvider(credentialsProvider)
.setDefaultRequestConfig(requestConfig)
.build();
在每个请求中使用相同的客户端实例:
HttpPost httppost = new HttpPost(endPoint.trim());
// HttpClientContext is not thread safe, one per request must be created.
HttpClientContext context = HttpClientContext.create();
response = this.httpclient.execute(httppost, context);
取消分配资源并将连接返回给连接管理器,在我的EJB的@PreDestroy方法中:
this.httpclient.close();
答案 1 :(得分:5)
我在使用HttpClient4.1.X时遇到了同样的问题 HttpClient 4.2.6它像魅力一样唤醒。以下是我的代码
DefaultHttpClient httpclient = new DefaultHttpClient();
HttpContext localContext = new BasicHttpContext();
HttpGet httpget = new HttpGet("url");
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(AuthScope.ANY,
new NTCredentials("username", "pwd", "", "domain"));
List<String> authtypes = new ArrayList<String>();
authtypes.add(AuthPolicy.NTLM);
httpclient.getParams().setParameter(AuthPNames.TARGET_AUTH_PREF,authtypes);
localContext.setAttribute(ClientContext.CREDS_PROVIDER, credsProvider);
HttpResponse response = httpclient.execute(httpget, localContext);
HttpEntity entity=response.getEntity();
答案 2 :(得分:3)
我遇到了与HttpClient 4.1.2类似的问题。对我来说,通过恢复到HttpClient 4.0.3解决了这个问题。使用内置实现或使用JCIFS,我永远无法让NTLM使用4.1.2。
答案 3 :(得分:2)
我发现解决此类问题的最简单方法是Wireshark。这是一个非常大的锤子,但它确实会向你展示一切。安装它,确保您的服务器在另一台机器上(不能与Localhost一起使用)并开始记录。
运行失败的请求,运行一个有效的请求。然后,按http过滤(只需在过滤器字段中输入http),找到第一个GET请求,找到另一个GET请求并进行比较。确定有意义的差异,您现在有特定的关键字或问题来搜索代码/网络。如果还不够,请缩小到第一个TCP对话并查看完整的请求/响应。与另一个相同。
我用这种方法解决了令人难以置信的问题。而Wireshark是非常有用的工具。许多超级高级功能使您的网络调试更容易。
您也可以在客户端或服务器端运行它。无论什么都会向您展示允许您进行比较的两个请求。
答案 4 :(得分:2)
更新我们的应用程序以使用httpcomponents-client-4.5.1中的jar解决了这个问题。
答案 5 :(得分:1)
我终于明白了。摘要式身份验证要求如果在请求中使用完整URL,则代理还需要使用完整URL。我没有在代码中留下代理代码,但它被定向到“localhost”,导致它失败。将其更改为127.0.0.1使其正常工作。