给HttpClient添加Socks代理(2)

场景:运行httpClient的进程所在主机可能并不能上公网,大部分时候,也无法进行DNS解析,这时通常会出现域名无法解析的IO异常,下面介绍怎么避免在客户端解析域名。

上面有一行代码非常关键:
remoteAddress = InetSocketAddress
                        .createUnresolved(host.getHostName(), host.getPort());

变量host是你发起http请求的目标主机和端口信息,这里创建了一个未解析(Unresolved)的SocketAddress,在socks协议握手阶段,InetSocketAddress信息会原封不动的发送到代理服务器,由代理服务器解析出具体的IP地址。
Socks的协议描述中有个片段:
  The SOCKS request is formed as follows:

+----+-----+-------+------+----------+----------+
        |VER | CMD |  RSV  | ATYP | DST.ADDR | DST.PORT |
        +----+-----+-------+------+----------+----------+
        | 1  |  1  | X'00' |  1  | Variable |    2    |
        +----+-----+-------+------+----------+----------+

Where:

o  VER    protocol version: X'05'
          o  CMD
            o  CONNECT X'01'
            o  BIND X'02'
            o  UDP ASSOCIATE X'03'
          o  RSV    RESERVED
          o  ATYP  address type of following address
            o  IP V4 address: X'01'
            o  DOMAINNAME: X'03'
            o  IP V6 address: X'04'

代码按上面方法写,协议握手发送的是ATYP=X'03',即采用域名的地址类型。否则,HttpClient会尝试在客户端解析,然后发送ATYP=X'01'进行协商。当然,大多数时候HttpClient在解析域名的时候就挂了。

https中需要注意的问题

在使用httpclient访问https网站的时候,经常会遇到javax.net.ssl包中的异常,例如:
Caused by: javax.net.ssl.SSLException: Received fatal alert: internal_error
    at sun.security.ssl.Alerts.getSSLException(Unknown Source) ~[na:1.7.0_80]
    at sun.security.ssl.Alerts.getSSLException(Unknown Source) ~[na:1.7.0_80]

一般需要做几个设置:

创建不校验证书链的SSLContext
        SSLContext sslContext = null;
        try {
            sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
                @Override
                public boolean isTrusted(X509Certificate[] chain, String authType)
                        throws CertificateException {
                    return true;
                }

}).build();
        } catch (Exception e) {
            throw new com.aliyun.oss.ClientException(e.getMessage());
        }
        ...
        new SocksSSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE)

创建不校验域名的HostnameVerifier
public class NoopHostnameVerifier implements javax.net.ssl.HostnameVerifier {

public static final NoopHostnameVerifier INSTANCE = new NoopHostnameVerifier();

@Override
    public boolean verify(final String s, final SSLSession sslSession) {
        return true;
    }
}

如何使用用户密码授权?

java SDK中给Socks代理授权有点特殊,不是按socket来的,而是在系统层面做的全局配置。比如,可以通过下面代码设置一个全局的Authenticator:
Authenticator.setDefault(new MyAuthenticator("userName", "Password"));
...
class MyAuthenticator extends java.net.Authenticator {
    private String user ;
    private String password ;
 
    public MyAuthenticator(String user, String password) {
      this.user = user;
      this.password = password;
    }
 
    protected PasswordAuthentication getPasswordAuthentication() {
      return new PasswordAuthentication(user, password.toCharArray());
    }
  }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/4ee34dd8253ac39c6cb9cf33723b1f37.html