Java 如何将Socks 5代理与Apache HTTP客户端4一起使用?
我正在尝试创建一个应用程序,通过ApacheHC4通过SOCKS5代理发送Java 如何将Socks 5代理与Apache HTTP客户端4一起使用?,java,dns,apache-httpclient-4.x,socks,Java,Dns,Apache Httpclient 4.x,Socks,我正在尝试创建一个应用程序,通过ApacheHC4通过SOCKS5代理发送HTTP请求。 我无法使用应用程序全局代理,因为应用程序是多线程的(我需要为每个HttpClient实例使用不同的代理)。我没有发现袜子5使用HC4的例子。如何使用它?SOCK是TCP/IP级别的代理协议,而不是HTTP。HttpClient开箱即用不支持它 可以使用自定义连接套接字工厂定制HttpClient以通过SOCKS代理建立连接 编辑:更改为SSL而不是普通套接字 Registry<ConnectionSo
HTTP
请求。
我无法使用应用程序全局代理,因为应用程序是多线程的(我需要为每个
HttpClient
实例使用不同的代理)。我没有发现袜子5使用HC4的例子。如何使用它?SOCK是TCP/IP级别的代理协议,而不是HTTP。HttpClient开箱即用不支持它
可以使用自定义连接套接字工厂定制HttpClient以通过SOCKS代理建立连接
编辑:更改为SSL而不是普通套接字
Registry<ConnectionSocketFactory> reg = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.INSTANCE)
.register("https", new MyConnectionSocketFactory(SSLContexts.createSystemDefault()))
.build();
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(reg);
CloseableHttpClient httpclient = HttpClients.custom()
.setConnectionManager(cm)
.build();
try {
InetSocketAddress socksaddr = new InetSocketAddress("mysockshost", 1234);
HttpClientContext context = HttpClientContext.create();
context.setAttribute("socks.address", socksaddr);
HttpHost target = new HttpHost("localhost", 80, "http");
HttpGet request = new HttpGet("/");
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());
} finally {
response.close();
}
} finally {
httpclient.close();
}
上面的答案非常有效,除非你的国家也毒害了DNS记录。很难说Java“在通过代理连接时不要使用我的DNS服务器”,如以下两个问题所述: 这对于ApacheHttpClient来说也很困难,因为它还尝试在本地解析主机名。通过对上述代码进行一些修改,可以解决这一问题:
static class FakeDnsResolver implements DnsResolver {
@Override
public InetAddress[] resolve(String host) throws UnknownHostException {
// Return some fake DNS record for every request, we won't be using it
return new InetAddress[] { InetAddress.getByAddress(new byte[] { 1, 1, 1, 1 }) };
}
}
static class MyConnectionSocketFactory extends PlainConnectionSocketFactory {
@Override
public Socket createSocket(final HttpContext context) throws IOException {
InetSocketAddress socksaddr = (InetSocketAddress) context.getAttribute("socks.address");
Proxy proxy = new Proxy(Proxy.Type.SOCKS, socksaddr);
return new Socket(proxy);
}
@Override
public Socket connectSocket(int connectTimeout, Socket socket, HttpHost host, InetSocketAddress remoteAddress,
InetSocketAddress localAddress, HttpContext context) throws IOException {
// Convert address to unresolved
InetSocketAddress unresolvedRemote = InetSocketAddress
.createUnresolved(host.getHostName(), remoteAddress.getPort());
return super.connectSocket(connectTimeout, socket, host, unresolvedRemote, localAddress, context);
}
}
static class MySSLConnectionSocketFactory extends SSLConnectionSocketFactory {
public MySSLConnectionSocketFactory(final SSLContext sslContext) {
// You may need this verifier if target site's certificate is not secure
super(sslContext, ALLOW_ALL_HOSTNAME_VERIFIER);
}
@Override
public Socket createSocket(final HttpContext context) throws IOException {
InetSocketAddress socksaddr = (InetSocketAddress) context.getAttribute("socks.address");
Proxy proxy = new Proxy(Proxy.Type.SOCKS, socksaddr);
return new Socket(proxy);
}
@Override
public Socket connectSocket(int connectTimeout, Socket socket, HttpHost host, InetSocketAddress remoteAddress,
InetSocketAddress localAddress, HttpContext context) throws IOException {
// Convert address to unresolved
InetSocketAddress unresolvedRemote = InetSocketAddress
.createUnresolved(host.getHostName(), remoteAddress.getPort());
return super.connectSocket(connectTimeout, socket, host, unresolvedRemote, localAddress, context);
}
}
publicstaticvoidmain(字符串[]args)引发异常{
Registry reg=RegistryBuilder.create()
.register(“http”,新的MyConnectionSocketFactory())
.register(“https”,新的MySSLConnectionSocketFactory(SSLContexts.createSystemDefault()).build();
PoolightPClientConnectionManager cm=新的PoolightPClientConnectionManager(reg,new FakeDnsResolver());
CloseableHttpClient httpclient=HttpClients.custom().setConnectionManager(cm.build();
试一试{
InetSocketAddress socksaddr=新的InetSocketAddress(“mysockshost”,1234);
HttpClientContext=HttpClientContext.create();
context.setAttribute(“socks.address”,socksaddr);
HttpGet请求=新建HttpGet(“https://www.funnyordie.com");
System.out.println(“通过SOCKS代理执行请求”+请求+”+socksaddr);
CloseableHttpResponse response=httpclient.execute(请求,上下文);
试一试{
System.out.println(“--------------------------------------------------------”;
System.out.println(response.getStatusLine());
int i=-1;
InputStream=response.getEntity().getContent();
而((i=stream.read())!=-1){
系统输出打印((字符)i);
}
使用(response.getEntity());
}最后{
response.close();
}
}最后{
httpclient.close();
}
}
灵感来自@oleg的答案。您可以创建一个实用程序,该实用程序为您提供了一个正确配置的CloseableHttpClient,并且对如何调用它没有特殊限制
您可以使用ConnectionSocketFactory中的ProxySelector来选择代理
用于构造CloseableHttpClient实例的实用程序类:
import org.apache.http.HttpHost;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.HttpCoreContext;
import org.apache.http.ssl.SSLContexts;
import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.net.*;
public final class HttpHelper {
public static CloseableHttpClient createClient()
{
Registry<ConnectionSocketFactory> reg = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", ProxySelectorPlainConnectionSocketFactory.INSTANCE)
.register("https", new ProxySelectorSSLConnectionSocketFactory(SSLContexts.createSystemDefault()))
.build();
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(reg);
return HttpClients.custom()
.setConnectionManager(cm)
.build();
}
private enum ProxySelectorPlainConnectionSocketFactory implements ConnectionSocketFactory {
INSTANCE;
@Override
public Socket createSocket(HttpContext context) {
return HttpHelper.createSocket(context);
}
@Override
public Socket connectSocket(int connectTimeout, Socket sock, HttpHost host, InetSocketAddress remoteAddress, InetSocketAddress localAddress, HttpContext context) throws IOException {
return PlainConnectionSocketFactory.INSTANCE.connectSocket(connectTimeout, sock, host, remoteAddress, localAddress, context);
}
}
private static final class ProxySelectorSSLConnectionSocketFactory extends SSLConnectionSocketFactory {
ProxySelectorSSLConnectionSocketFactory(SSLContext sslContext) {
super(sslContext);
}
@Override
public Socket createSocket(HttpContext context) {
return HttpHelper.createSocket(context);
}
}
private static Socket createSocket(HttpContext context) {
HttpHost httpTargetHost = (HttpHost) context.getAttribute(HttpCoreContext.HTTP_TARGET_HOST);
URI uri = URI.create(httpTargetHost.toURI());
Proxy proxy = ProxySelector.getDefault().select(uri).iterator().next();
return new Socket(proxy);
}
}
如果您知道哪些URI需要转到代理,还可以使用低层ProxySelector:对于每个套接字连接,您可以决定要使用哪些代理 它看起来像这样:
public class MyProxySelector extends ProxySelector {
...
public java.util.List<Proxy> select(URI uri) {
...
if (uri is what I need) {
return list of my Proxies
}
...
}
...
}
好的,谢谢。我尝试使用此代码,但我的目标主机是https。如果我将“https”作为方案传递给HttpHost构造函数,它会说不支持方案“https”。如果我传递“http”,它会说响应无效(因为我认为它是SSL),这并没有多大区别,因为SOCKS是TCP/IP级别的协议。请参阅更新的代码片段
HttpClientContext=HttpClientContext.create()
未使用直接复制粘贴您的代码,一开始它对我不起作用。然后我意识到,在我的例子中,我必须在http
上注册一个定制的基于SOCK的PlainConnectionSocketFactory
,然后它才能工作。如果其他人也有同样的问题,请在此处发布。是否可以使用socks5代理进行身份验证?我尝试使用CredentialsProvider
,但没有成功。httpclient似乎忽略了提供的凭据,而是使用我的本地用户名。这是我所发现的最有用的信息之一。任何想通过SOCKS代理使用HttpClient并需要让SOCKS代理解析主机名的人,都可以这样做。@b10y代码中有一个错误。我发布了一个新问题。你能看一下吗?
import org.apache.http.HttpHost;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.HttpCoreContext;
import org.apache.http.ssl.SSLContexts;
import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.net.*;
public final class HttpHelper {
public static CloseableHttpClient createClient()
{
Registry<ConnectionSocketFactory> reg = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", ProxySelectorPlainConnectionSocketFactory.INSTANCE)
.register("https", new ProxySelectorSSLConnectionSocketFactory(SSLContexts.createSystemDefault()))
.build();
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(reg);
return HttpClients.custom()
.setConnectionManager(cm)
.build();
}
private enum ProxySelectorPlainConnectionSocketFactory implements ConnectionSocketFactory {
INSTANCE;
@Override
public Socket createSocket(HttpContext context) {
return HttpHelper.createSocket(context);
}
@Override
public Socket connectSocket(int connectTimeout, Socket sock, HttpHost host, InetSocketAddress remoteAddress, InetSocketAddress localAddress, HttpContext context) throws IOException {
return PlainConnectionSocketFactory.INSTANCE.connectSocket(connectTimeout, sock, host, remoteAddress, localAddress, context);
}
}
private static final class ProxySelectorSSLConnectionSocketFactory extends SSLConnectionSocketFactory {
ProxySelectorSSLConnectionSocketFactory(SSLContext sslContext) {
super(sslContext);
}
@Override
public Socket createSocket(HttpContext context) {
return HttpHelper.createSocket(context);
}
}
private static Socket createSocket(HttpContext context) {
HttpHost httpTargetHost = (HttpHost) context.getAttribute(HttpCoreContext.HTTP_TARGET_HOST);
URI uri = URI.create(httpTargetHost.toURI());
Proxy proxy = ProxySelector.getDefault().select(uri).iterator().next();
return new Socket(proxy);
}
}
import com.okta.tools.helpers.HttpHelper;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
import java.net.URI;
public class Main {
public static void main(String[] args) throws IOException {
URI uri = URI.create("http://example.com/");
HttpGet request = new HttpGet(uri);
try (CloseableHttpClient closeableHttpClient = HttpHelper.createClient()) {
try (CloseableHttpResponse response = closeableHttpClient.execute(request)) {
System.out.println("----------------------------------------");
System.out.println(response.getStatusLine());
System.out.println(EntityUtils.toString(response.getEntity()));
}
}
}
}
public class MyProxySelector extends ProxySelector {
...
public java.util.List<Proxy> select(URI uri) {
...
if (uri is what I need) {
return list of my Proxies
}
...
}
...
}
public static void main(String[] args) {
MyProxySelector ps = new MyProxySelector(ProxySelector.getDefault());
ProxySelector.setDefault(ps);
// rest of the application
}