我使用Apache HttpClient连接到运行Jetty并使用WAFFLE进行身份验证的Web服务器。我遇到问题,服务器发送三个WWW-Authenticate
标头(因为它支持三种身份验证方案),但随后在客户端上,CredentialsProvider
被要求三次获得相同的凭据。< / p>
如果我使用内置的Java URL
类连接到同一台服务器,它会获取资源而不会要求我提供凭据。
HTTP客户端设置非常简单:
RequestConfig requestConfig =
HttpClientUtils.createDefaultRequestConfig();
HttpClientBuilder builder = HttpClients.custom()
.setDefaultCredentialsProvider(credentialsProvider)
.setDefaultCookieStore(cookieStore)
.setDefaultRequestConfig(requestConfig);
return builder.build();
CredentialsProvider
是一个自定义的,它会委托我们自己的框架提示用户提供该信息。
public class AdaptedCredentialsProvider
implements CredentialsProvider
{
private final CredentialsProvider internal =
new BasicCredentialsProvider();
//I tried this too:
//new SystemDefaultCredentialsProvider();
private final CredentialsPrompt prompt;
public AdaptedCredentialsProvider(CredentialsPrompt prompt) {
this.prompt = prompt;
}
@Override
public void setCredentials(AuthScope authscope,
Credentials credentials) {
internal.setCredentials(authscope, credentials);
}
@Override
public Credentials getCredentials(@NotNull AuthScope authScope) {
System.err.println("Asked for credentials, scheme: " +
authScope.getScheme());
Credentials localCredentials =
internal.getCredentials(authScope);
if (localCredentials != null) {
return localCredentials;
}
if (authScope.getHost() != null) {
OurCredentials credentials = prompt.prompt(
authScope.getHost(),
authScope.getPort());
if (credentials != null) {
switch (authScope.getScheme()) {
case "NEGOTIATE":
case "NTLM":
return new NTCredentials(
credentials.getUsername(),
new String(credentials.getPassword()),
null, null);
default:
return new UsernamePasswordCredentials(
credentials.getUsername(),
new String(credentials.getPassword()));
}
}
}
return null;
}
@Override
public void clear() {
internal.clear();
}
}
请求和响应的时间表以及散布的登录请求很有意思......
=== Initial request
=== Request
GET /api/noop HTTP/1.1
Host: 192.168.1.162:27443
Connection: Keep-Alive
Accept-Encoding: gzip,deflate
=== Response
HTTP/1.1 401 Unauthorized
Date: Mon, 27 Apr 2015 02:44:23 GMT
Set-Cookie: JSESSIONID=1lsrefm3yzmmz15n8md7efdc7f;Path=/;Secure
WWW-Authenticate: Negotiate
WWW-Authenticate: NTLM
WWW-Authenticate: Basic realm="Test"
Cache-Control: must-revalidate,no-cache,no-store
Content-Type: text/html; charset=ISO-8859-1
Content-Length: 289
=== Asked for credentials for host: 192.168.1.162 - scheme: NEGOTIATE
=== GUI checking whether the credentials work...
=== Request
GET /api/noop HTTP/1.1
Host: 192.168.1.162:27443
Connection: Keep-Alive
Accept-Encoding: gzip,deflate
=== Response
HTTP/1.1 401 Unauthorized
Date: Mon, 27 Apr 2015 02:44:30 GMT
Set-Cookie: JSESSIONID=ucsnvsvawp301injh6ijwbywe;Path=/;Secure
WWW-Authenticate: Negotiate
WWW-Authenticate: NTLM
WWW-Authenticate: Basic realm="Test"
Cache-Control: must-revalidate,no-cache,no-store
Content-Type: text/html; charset=ISO-8859-1
Content-Length: 289
=== Request
GET /api/noop HTTP/1.1
Host: 192.168.1.162:27443
Connection: Keep-Alive
Accept-Encoding: gzip,deflate
Authorization: Basic <REDACTED>
=== Response
HTTP/1.1 200 OK
Date: Mon, 27 Apr 2015 02:44:30 GMT
Set-Cookie: JSESSIONID=1la5wb3fje1fy1qxu14weqxaqm;Path=/;Secure
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Content-Length: 0
=== Asked for credentials for host: 192.168.1.162 - scheme: NTLM
=== GUI checking whether the credentials work...
=== Request
GET /api/noop HTTP/1.1
Host: 192.168.1.162:27443
Connection: Keep-Alive
Accept-Encoding: gzip,deflate
=== Response
HTTP/1.1 401 Unauthorized
Date: Mon, 27 Apr 2015 02:44:33 GMT
Set-Cookie: JSESSIONID=5pmtxxhinky91bzoj18yx6zkz;Path=/;Secure
WWW-Authenticate: Negotiate
WWW-Authenticate: NTLM
WWW-Authenticate: Basic realm="Test"
Cache-Control: must-revalidate,no-cache,no-store
Content-Type: text/html; charset=ISO-8859-1
Content-Length: 289
=== Request
GET /api/noop HTTP/1.1
Host: 192.168.1.162:27443
Connection: Keep-Alive
Accept-Encoding: gzip,deflate
Authorization: Basic <REDACTED>
=== Response
HTTP/1.1 200 OK
Date: Mon, 27 Apr 2015 02:44:33 GMT
Set-Cookie: JSESSIONID=16jhmw3fxuhsy1edjtpquiw564;Path=/;Secure
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Content-Length: 0
=== Asked for credentials for host: 192.168.1.162 - scheme: BASIC
=== GUI checking whether the credentials work...
=== Request
GET /api/noop HTTP/1.1
Host: 192.168.1.162:27443
Connection: Keep-Alive
Accept-Encoding: gzip,deflate
=== Response
HTTP/1.1 401 Unauthorized
Date: Mon, 27 Apr 2015 02:44:36 GMT
Set-Cookie: JSESSIONID=2yjw3xcoerq5wdtqu1etxur0;Path=/;Secure
WWW-Authenticate: Negotiate
WWW-Authenticate: NTLM
WWW-Authenticate: Basic realm="Test"
Cache-Control: must-revalidate,no-cache,no-store
Content-Type: text/html; charset=ISO-8859-1
Content-Length: 289
=== Request
GET /api/noop HTTP/1.1
Host: 192.168.1.162:27443
Connection: Keep-Alive
Accept-Encoding: gzip,deflate
Authorization: Basic <REDACTED>
=== Response
HTTP/1.1 200 OK
Date: Mon, 27 Apr 2015 02:44:36 GMT
Set-Cookie: JSESSIONID=cvf6mvdagknk1mb893u7ylozb;Path=/;Secure
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Content-Length: 0
=== Presumably here it has decided it doesn't support NEGOTIATE so
=== it's going with NTLM.
=== Request
GET /api/noop HTTP/1.1
Host: 192.168.1.162:27443
Connection: Keep-Alive
Accept-Encoding: gzip,deflate
Authorization: NTLM <REDACTED>
=== Response
HTTP/1.1 401 Unauthorized
Date: Mon, 27 Apr 2015 02:44:36 GMT
Set-Cookie: JSESSIONID=es21dv6pnktmqoxr9qs52n3e;Path=/;Secure
Expires: Thu, 01 Jan 1970 00:00:00 GMT
WWW-Authenticate: NTLM <REDACTED>
Transfer-Encoding: chunked
0
=== Request
GET /api/noop HTTP/1.1
Host: 192.168.1.162:27443
Connection: Keep-Alive
Accept-Encoding: gzip,deflate
Authorization: NTLM <REDACTED>
=== Response
HTTP/1.1 200 OK
Date: Mon, 27 Apr 2015 02:44:36 GMT
Set-Cookie: JSESSIONID=1nzdiyw90zun218drgkobi3y6g;Path=/;Secure
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Content-Length: 0
似乎它甚至没有使用它所要求的凭据。最终它以某种方式突破了这个奇怪的循环,然后尝试使用它们。
它最终会登录,但我怎么能阻止它三次询问相同的信息? (我知道我可以在CredentialsProvider
中添加这类内容,但我没有必要使用其他API来实现这一点,这对于解决这个问题似乎是一种尴尬的方法。)< / p>
事实上,我怎么能阻止它要求提供这些信息呢?首先,使用Negotiate主要是为了避免被提示输入密码,但这件事总是提示我输入密码而且我认为不应该这样。
更新:问题的一半已经解决。我发现HttpClient 根本没有支持单点登录(有点误导,IMO,声称你支持NTLM而不支持使用NTLM的唯一目的!)但是版本4.4有实验支持它。
实验性支持似乎让我登录但仍然会提示我输入Basic的密码。所以我回到了如何禁用向凭证提供程序请求每个auth方案的这种行为的问题。理想情况下,它应该只是要求第一个,然后再回到序列中的下一个。
答案 0 :(得分:0)
首先,您应该缓存凭据而不是向用户询问N次。您可以使用具有超时的缓存来提高安全性,并避免将密码保留在内存中太长时间。
您还必须确保为每个请求使用相同的credentialsProvider
和cookieStore
。
我不确定为什么Java URL
可以连接。在代码中查找Authenticator.setDefault()
;没有它,Java的URL
类不能进行任何身份验证。
[编辑] 您的代码每次都要求输入密码的原因是因为您这样写了:
相关: