java HttpClient 4.2.2 和带有用户名/密码的代理

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13288038/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 12:07:48  来源:igfitidea点击:

HttpClient 4.2.2 and proxy with username/password

javaproxyhttpclientsquidhttp-status-code-407

提问by Lazy

I have a problem with HttpClient 4.2.2 and proxy with username/password. HttpClient works in this way:

我有 HttpClient 4.2.2 和用户名/密码代理的问题。HttpClient 以这种方式工作:

  1. List item
  2. Send request without proxy stuff (however I set proxy parameters for each request)
  3. Get response from Squid with 407 error
  4. Send request with proxy stuff
  1. 项目清单
  2. 发送没有代理的请求(但是我为每个请求设置了代理参数)
  3. 从 Squid 获得 407 错误的响应
  4. 使用代理发送请求

It's very strange behavior, Is it possible to add info about proxy to each request? I've tried to add hardcoded "Proxy-Authorization" header to each request and it works fine, why HttpClient can't do the same?

这是非常奇怪的行为,是否可以向每个请求添加有关代理的信息?我已经尝试向每个请求添加硬编码的“代理授权”标头并且它工作正常,为什么 HttpClient 不能做同样的事情?

Java code

Java代码

DefaultHttpClient httpClient = new DefaultHttpClient();
httpClient.getParams().setParameter(PROTOCOL_VERSION, HTTP_1_1);
...
String proxyServer = getProxyServer();
int proxyPort = getProxyPort();
List<String> authpref = new ArrayList<String>();
authpref.add(AuthPolicy.BASIC);
httpClient.getParams().setParameter(AuthPNames.PROXY_AUTH_PREF, authpref);
String proxyUser = getProxyUser();
String proxyPassword = getProxyPassword();
CredentialsProvider credsProvider = httpClient.getCredentialsProvider();
credsProvider.setCredentials(new AuthScope(proxyServer, proxyPort), new UsernamePasswordCredentials(proxyUser, proxyPassword));
httpClient.setCredentialsProvider(credsProvider);
HttpHost proxy = new HttpHost(proxyServer, proxyPort, (proxyServer.indexOf("https") != 0) ? "http" : "https");
httpClient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy);
...
HttpPut put = new HttpPut(url);

/*** hardcoded header ***/
//put.addHeader("Proxy-Authorization", "Basic eHRlbmR4LmRuZXByOnF3ZXJ0eQ==");
/*** hardcoded header ***/

put.setEntity(entity);
httpClient.execute(put);
httpClient.getConnectionManager().shutdown();


[DefaultClientConnection] Sending request: PUT http://172.26.27.22:8080/myapp/rest/2/3/29/image1.jpg HTTP/1.1
[headers] >> PUT http://172.26.27.22:8080/myapp/rest/2/3/29/image1.jpg HTTP/1.1
[headers] >> Authorization: Basic eGRldjo0YTFmNmMwOTgyYWRkMWQ0NDg1YjRhMGE4YWMxY2JjMWNiMTA0ODc1
[headers] >> Content-Length: 2
[headers] >> Host: 172.26.27.22:8080
[headers] >> Proxy-Connection: Keep-Alive
[headers] >> User-Agent: Apache-HttpClient/4.2.2 (java 1.5)
[DefaultClientConnection] Receiving response: HTTP/1.0 407 Proxy Authentication Required
[headers] << HTTP/1.0 407 Proxy Authentication Required
[headers] << Server: squid/2.7.STABLE8
[headers] << Date: Thu, 08 Nov 2012 10:09:49 GMT
[headers] << Content-Type: text/html
[headers] << Content-Length: 1431
[headers] << X-Squid-Error: ERR_CACHE_ACCESS_DENIED 0
[headers] << Proxy-Authenticate: Basic realm="Please, enter username and password"
[headers] << X-Cache: MISS from 172.26.27.94
[headers] << X-Cache-Lookup: NONE from 172.26.27.94:3128
[headers] << Via: 1.0 172.26.27.94:3128 (squid/2.7.STABLE8)
[headers] << Connection: close
[DefaultHttpClient] Authentication required
[DefaultHttpClient] 172.26.27.94:3128 requested authentication
[ProxyAuthenticationStrategy] Authentication schemes in the order of preference: [Basic]
[DefaultHttpClient] Selected authentication options: [BASIC]
[DefaultClientConnection] Connection 0.0.0.0:63344<->172.26.27.94:3128 closed
[DefaultClientConnectionOperator] Connecting to 172.26.27.94:3128
[RequestAddCookies] CookieSpec selected: best-match
[RequestAuthCache] Re-using cached 'basic' auth scheme for http://172.26.27.22:8080
[RequestAuthCache] No credentials for preemptive authentication
[RequestProxyAuthentication] Proxy auth state: CHALLENGED
[RequestProxyAuthentication] Generating response to an authentication challenge using basic scheme
[DefaultHttpClient] Attempt 2 to execute request
[DefaultClientConnection] Sending request: PUT http://172.26.27.22:8080/myapp/rest/2/3/29/image1.jpg HTTP/1.1
[headers] >> PUT http://172.26.27.22:8080/myapp/rest/2/3/29/image1.jpg HTTP/1.1
[headers] >> Authorization: Basic eGRldjo0YTFmNmMwOTgyYWRkMWQ0NDg1YjRhMGE4YWMxY2JjMWNiMTA0ODc1
[headers] >> Content-Length: 2
[headers] >> Host: 172.26.27.22:8080
[headers] >> Proxy-Connection: Keep-Alive
[headers] >> User-Agent: Apache-HttpClient/4.2.2 (java 1.5)
[headers] >> Proxy-Authorization: Basic eHRlbmR4LmRuZXByOnF3ZXJ0eQ==
[DefaultClientConnection] Receiving response: HTTP/1.0 201 Created
[headers] << HTTP/1.0 201 Created
[headers] << Content-Length: 0
[headers] << Date: Thu, 08 Nov 2012 10:09:49 GMT
[headers] << X-Cache: MISS from 172.26.27.94
[headers] << X-Cache-Lookup: MISS from 172.26.27.94:3128
[headers] << Via: 1.1 172.26.27.94:3128 (squid/2.7.STABLE8)
[headers] << Connection: keep-alive
[headers] << Proxy-Connection: keep-alive

Squid log

鱿鱼日志

1352370666.778      0 172.26.27.94 TCP_DENIED/407 1870 PUT http://172.26.27.22:8080/myapp/rest/2/3/29/image1.jpg - NONE/- text/html
1352370671.429      8 172.26.27.94 TCP_MISS/201 282 PUT http://172.26.27.22:8080/myapp/rest/2/3/29/image1.jpg proxyuser DIRECT/172.26.27.22 -
1352370671.474      0 172.26.27.94 TCP_DENIED/407 1882 PUT http://172.26.27.22:8080/myapp/rest/2/3/29/image2.jpg - NONE/- text/html
1352370671.486      7 172.26.27.94 TCP_MISS/201 282 PUT http://172.26.27.22:8080/myapp/rest/2/3/29/image2.jpg proxyuser DIRECT/172.26.27.22 -

With hardcoded header

带有硬编码的标头

There is no 407 error in the java log, and squid log

java日志没有407错误,squid日志

Squid log

鱿鱼日志

1352370542.016      8 172.26.27.94 TCP_MISS/201 282 PUT http://172.26.27.22:8080/myapp/rest/2/3/29/image1.jpg proxyuser DIRECT/172.26.27.22 -
1352370542.033      7 172.26.27.94 TCP_MISS/201 282 PUT http://172.26.27.22:8080/myapp/rest/2/3/29/image2.jpg proxyuser DIRECT/172.26.27.22 -

回答by Cristian Greco

The authentication process with an HTTP proxy is described in RFC2616 §14.33and §14.34 and is exactly as you see with HttpClient. It includes:

使用 HTTP 代理的身份验证过程在 RFC2616 §14.33和 §14.34 中描述,与您在 HttpClient 中看到的完全一样。这包括:

  • the proxy sending a 407 (Proxy Authentication Required)response with a Proxy-Authenticateheader containing the challenge applicable to the requested resource,
  • the client issuing a new request with a Proxy-Authorizationheader consisting of the credentials containing the authentication information.
  • 代理发送407(需要代理身份验证)响应,其中包含适用于所请求资源的质询的Proxy-Authenticate标头,
  • 客户端发出一个带有代理授权标头的新请求,该标头由包含身份验证信息的凭据组成。

Implementing the preemptive authentication with HttpClient is described in this tutorial(section §4.8), and requires prepopulating the authentication cache of the client object. Unfortunately, their code does not work when preauthenticating to a proxy server. It may be a bit tricky to understand how to get it right, but actually it is as simple as passing a parameter to the BasicSchemeconstructor:

本教程(第 4.8 节)描述了使用 HttpClient 实现抢占式身份验证,并且需要预先填充客户端对象的身份验证缓存。不幸的是,他们的代码在对代理服务器进行预身份验证时不起作用。理解如何正确处理可能有点棘手,但实际上就像将参数传递给BasicScheme构造函数一样简单:

AuthCache authCache = new BasicAuthCache();

AuthScheme basicAuthScheme = null;
if (isProxy) {
   basicAuthScheme = new BasicScheme(ChallengeState.PROXY);
} else {
   basicAuthScheme = new BasicScheme(ChallengeState.TARGET);
}

authCache.put(host, basicAuthScheme);
httpContext.setAttribute(ClientContext.AUTH_CACHE, authCache);

回答by Dave G

It's your AuthScope:

这是您的 AuthScope:

// You set proxyServer here vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
credsProvider.setCredentials(new AuthScope(proxyServer, proxyPort), new UsernamePasswordCredentials(proxyUser, proxyPassword));
httpClient.setCredentialsProvider(credsProvider);
// But here you are looking for the indexof https to determine if it is an SSL proxy
// is the String returned from getProxyServer() above a URL or a host name?
HttpHost proxy = new HttpHost(proxyServer, proxyPort, (proxyServer.indexOf("https") != 0) ? "http" : "https");
httpClient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy);

Buried in my comment above - is the string returned by getProxyServer() a URL or a hostname?

埋在我上面的评论中 - getProxyServer() 返回的字符串是 URL 还是主机名?

EDIT

编辑

I think I have figured out what is going on. Basic Auth has to be sent on every request for it to work. If you are making multiple executions you are going to need to have some mechanism to cache the authentication data. By executing the client in the fashion you are right now, there is no "context" that can store that as one is created on every request.

我想我已经弄清楚发生了什么。必须在每个请求上发送基本身份验证才能使其工作。如果您要进行多次执行,您将需要某种机制来缓存身份验证数据。通过以您现在的方式执行客户端,没有“上下文”可以存储它,因为每个请求都会创建一个。

The other item to look at is the client tutorialsections 4.7 and 4.8. If you are looking to completely eliminate the 407 error followed by a request with the BASIC authentication, then make sure you read section 4.8.

要查看的另一项是客户端教程第 4.7 和 4.8 节。如果您希望完全消除 407 错误,然后是使用 BASIC 身份验证的请求,那么请务必阅读第 4.8 节。

Pay very close attention to the "localcontext" variable they have defined as this acts as a state container for your client.

密切注意他们定义的“localcontext”变量,因为它充当客户端的状态容器。