转载

开发技巧-Java通过HttpProxy实现穿越

需求描述

在正常的项目开发需求中,连接远程服务器的场景一般有二:

1  自家实现的http服务器,api接口都已经约定好;

2  开发平台服务,通常如新浪、百度云等平台提供的restful接口;

以上的两种场景通过原生的URLConnection或是apache提供的httpclient工具包都可以方便的实现调用。

然而,第三种场景是需要连接国外的开放服务,如google、twitter、tumblr等开放API接口。

在伟大的gfw关怀下,我们被告知不要随便和陌生人说话...

好吧,接下来让我们开始实现基于proxy的穿越吧!

准备工作

1  http代理服务器

建议花点银子买个稳定的VPN,带http代理的那种。

2  外网访问测试

可以用chrome switchyOmega插件测试一把,不行直接设置IE系统代理

准备完毕,可以开始开发了

设计分析

代理连接实现的关键步骤:

一、设置代理服务器地址端口

方式一: Java支持以System.setProperty的方式设置http代理及端口,如下:

System.setProperty("http.proxySet", "true"); System.setProperty("http.proxyHost", proxyHost); System.setProperty("http.proxyPort", "" + proxyPort);   // 针对https也开启代理 System.setProperty("https.proxyHost", proxyHost); System.setProperty("https.proxyPort", "" + proxyPort);

关于Java属性的详细设置可参考: http://docs.oracle.com/javase/6/docs/technotes/guides/net/properties.html

方式二: 使用Proxy对象,在建立连接时注入到URLConnection即可:

// 初始化proxy对象 Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyPort));   // 创建连接 URL u = new URL(url); URLConnection conn = u.openConnection(proxy);

关于两种方式的比较

第一种方式更值得推荐,当你采用基于URLConnection封装实现的类库时,采用setProperty的方式则不需要动里面的代码,绿色轻便。

二、实现用户密码校验

方式一: 将校验信息写入http头,将用户名密码进行base64编码之后设置Proxy-Authorization头:

String headerKey = "Proxy-Authorization"; String encoded = new String(Base64.encodeBase64((new String(proxyUser + ":" + proxyPass).getBytes()))); String headerValue = "Basic " + encoded; conn.setRequestProperty(headerKey, headerValue);

不少资料会推荐这样的方式,但经过测试, 该方式在https的需求场景下无法正常工作

    方式二:实现Authenticator接口,并注入为全局验证器:

public static class MyAuthenticator extends Authenticator {  String userName;  String password; public MyAuthenticator (String userName, String password) {  this.userName = userName;  this.password = password; } /** * 当需要使用密码校验时自动触发 */ @Override protected PasswordAuthentication getPasswordAuthentication() {  return new PasswordAuthentication(userName, password.toCharArray()); } } 

在执行连接之前注入校验实例:

MyAuthenticator auth = new MyAuthenticator(proxyUser, proxyPass); Authenticator.setDefault(auth);

实例代码

入口类

/**  * 网络代理测试  *   * <pre>  * 设置代理主机及端口:系统变量(https 需同步设置)  * 设置代理验证方式:全局代理对象  *   *   * https链接错误:  * Unable to tunnel through proxy. Proxy returns "HTTP/1.0 407 Proxy Authentication Required"  * 使用全局代理验证解决  *   * </pre>  *   * @author tzz  * @createDate 2015年7月23日  *   */ public class ProxyTest {     private static String proxyHost = "xxx.xxxxx.com";     private static int proxyPort = 8080;     private static String proxyUser = "user";     private static String proxyPass = "pass";     public static void main(String[] args) {         String url = "https://www.google.com/";         String content = doProxy(url);         System.out.println("Result :===================/n " + content);     }     /**      * 通过系统变量方式实现代理      *       * @param url      * @return      */     public static String doProxy(String url) {         // 设置系统变量          System.setProperty("http.proxySet", "true");         System.setProperty("http.proxyHost", proxyHost);         System.setProperty("http.proxyPort", "" + proxyPort);         // 针对https也开启代理         System.setProperty("https.proxyHost", proxyHost);         System.setProperty("https.proxyPort", "" + proxyPort);         // 设置默认校验器         setDefaultAuthentication();          //开始请求         try {             URL u = new URL(url);             URLConnection conn = u.openConnection();             HttpsURLConnection httpsCon = (HttpsURLConnection) conn;             httpsCon.setFollowRedirects(true);              String encoding = conn.getContentEncoding();             if (StringUtils.isEmpty(encoding)) {                 encoding = "UTF-8";             }             InputStream is = conn.getInputStream();             String content = IOUtils.toString(is, encoding);             return content;         } catch (Exception e) {             e.printStackTrace();             return e.getMessage();         }     }      /**      * 设置全局校验器对象      */     public static void setDefaultAuthentication() {         BasicAuthenticator auth = new BasicAuthenticator(proxyUser, proxyPass);         Authenticator.setDefault(auth);     } }

校验器

/**  * 实现sun.net的代理验证  *   * @author tzz  * @createDate 2015年7月23日  *   */ public static class BasicAuthenticator extends Authenticator {     String userName;     String password;     public BasicAuthenticator(String userName, String password) {         this.userName = userName;         this.password = password;     }     /**      * Called when password authorization is needed. Subclasses should override the default implementation, which returns null.      *       * @return The PasswordAuthentication collected from the user, or null if none is provided.      */     @Override     protected PasswordAuthentication getPasswordAuthentication() {         //System.out.println("DEBUG === use global authentication of password");         return new PasswordAuthentication(userName, password.toCharArray());     } } 

常见问题

连接时异常

Unable to tunnel through proxy. Proxy returns "HTTP/1.0 407 Proxy Authentication Required"

通常是代理服务器未能读取到验证信息所致,请检查目标url是否为https连接以及全局的Authenticator类是否正确设置。

正文到此结束
Loading...