具有身份验证的 Apache HttpClient Socks5 代理

Posted

技术标签:

【中文标题】具有身份验证的 Apache HttpClient Socks5 代理【英文标题】:Apache HttpClient Socks5 proxy with authentication 【发布时间】:2018-07-10 15:30:05 【问题描述】:

我正在尝试使用 Socks5 代理服务器来请求在我的 Java 应用程序中需要代理的特定目标(主机)。我正在使用 Apache Http 库(v. 4.5.4)。我有许多不需要代理的其他目标,因此为整个应用程序全局设置代理对我来说不是一个选择。所以我为HttpClient 的某个实例设置了代理。代理需要登录并通过身份验证。

我的解决方案是基于此question,另外添加documentation 中所述的身份验证参数。

这是我的代码:

private void sendSocks5Request(StringEntity requestEntity) throws Exception 

        Registry<ConnectionSocketFactory> reg = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("http", PlainConnectionSocketFactory.INSTANCE)
                .register("https", new MyConnectionSocketFactory(SSLContexts.createSystemDefault()))
                .build();
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(reg);

        Credentials credentials = new UsernamePasswordCredentials("proxy_user","proxy_pass");
        AuthScope authScope = new AuthScope("my.proxy.com", 1080);
        CredentialsProvider credsProvider = new BasicCredentialsProvider();
        credsProvider.setCredentials(authScope, credentials);

        CloseableHttpClient httpclient = HttpClients.custom()
                .setConnectionManager(cm)
                .setDefaultCredentialsProvider(credsProvider)
                .build();

        try 
            InetSocketAddress socksaddr = new InetSocketAddress("my.proxy.com", 1080);
            HttpClientContext context = HttpClientContext.create();
            context.setAttribute("socks.address", socksaddr);

            HttpHost target = new HttpHost("api.telegram.org", 80, "https");
            HttpPost request = new HttpPost("/bot<bot_token_here>");
            request.setEntity(requestEntity);

            System.out.println("Executing request " + request + " to " + target + " via SOCKS proxy " + socksaddr);
            CloseableHttpResponse response = httpclient.execute(target, request, context);
            try 
                System.out.println("----------------------------------------");
                System.out.println(response.getStatusLine());
                EntityUtils.consume(response.getEntity());
             catch (Exception ex) 
                ex.getMessage();
             finally 
                response.close();
            
         catch (Exception ex) 
            ex.getMessage();
         finally 
            httpclient.close();
        

代理似乎工作,但我得到一个Authentication failed 错误。代理服务器本身的日志显示,提供的身份验证不包含proxy_user 用户,而是我机器的系统用户。所以看起来 HttpClient 忽略了提供的凭据并从系统中获取它。

我做错了什么?

【问题讨论】:

我们有同样的问题,我们使用的代理不需要身份验证,但是因为我们的 java 代码本质上是发送系统用户(这实际上是一个安全问题,因为它提供了目标系统不提供的信息需要!!!)代理会抛出一个错误,导致整个通信失败。你有办法吗?看来这个问题是唯一解决这个问题的! 【参考方案1】:

我花了几天的时间来解决同样的问题。 对我来说,最简单的方法是使用java的Authenticator 只需在execute之前添加此代码:

Authenticator.setDefault(new Authenticator() 
    protected PasswordAuthentication getPasswordAuthentication() 
        return new PasswordAuthentication(proxyUser, proxyPassword.toCharArray());
    
);

之后,套接字应该发送您的凭据。有兴趣可以调试java.net.SocksSocketImpl#authenticate(byte, java.io.InputStream, java.io.BufferedOutputStream, long)方法。

我尝试使用许多其他方法,但它们都不适合我。我假设setDefaultCredentialsProvider 正在响应target 上的身份验证,而不是proxy。我还没有调试得那么深。我仍然试图弄清楚如何更优雅地解决这个问题。如果您能找到更好的解决方案,请告诉我。

【讨论】:

以上是关于具有身份验证的 Apache HttpClient Socks5 代理的主要内容,如果未能解决你的问题,请参考以下文章

Apache HttpClient 4.3 和 x509 客户端证书进行身份验证

Apache HttpClient 4.3.1 中使用 HTTP 隧道/HTTPS 连接的抢先式代理身份验证

具有不同身份验证标头的 HttpClient 单个实例

具有摘要身份验证的 HttpClient 发布请求导致错误请求

使用 HttpClient 4 通过 WiFi 进行 HTTPS 身份验证

使用 HttpClient 类时如何对代理进行身份验证?