HttpClient 4.2.2和代理用户名/密码

时间:2012-11-08 11:18:56

标签: java proxy httpclient squid http-status-code-407

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

  1. 列表项
  2. 发送没有代理服务器的请求(但是我为每个请求设置了代理参数)
  3. 从407收到来自Squid的回复
  4. 使用代理服务器发送请求
  5. 这是非常奇怪的行为,是否可以为每个请求添加有关代理的信息?我试图为每个请求添加硬编码的“代理授权”标头,它工作正常,为什么HttpClient不能这样做?

    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
    

    鱿鱼日志

    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 -
    

    使用硬编码标题

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

    鱿鱼日志

    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 -
    

2 个答案:

答案 0 :(得分:5)

RFC2616 §14.33和§14.34中描述了使用HTTP代理的身份验证过程,正如您在HttpClient中看到的那样。它包括:

  • 代理使用包含适用于所请求资源的质询的代理验证标头发送 407(需要代理验证)响应,
  • 客户端使用代理授权标头发出新请求,该标头包含包含身份验证信息的凭据。

this tutorial(第§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);

答案 1 :(得分:0)

这是你的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);

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

修改

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

要查看的另一个项目是client tutorial部分4.7和4.8。如果您希望完全消除407错误,然后使用BASIC身份验证请求,请确保阅读第4.8节。

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