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代理及端口,如下:
 
[java]  view plain  copy
  1.       
  2. System.setProperty("http.proxySet""true");  
  3. System.setProperty("http.proxyHost", proxyHost);  
  4. System.setProperty("http.proxyPort""" + proxyPort);  
  5.    
  6. // 针对https也开启代理  
  7. System.setProperty("https.proxyHost", proxyHost);  
  8. System.setProperty("https.proxyPort""" + proxyPort);  

  

       关于Java属性的详细设置可参考: http://docs.oracle.com/javase/6/docs/technotes/guides/net/properties.html
 
 
          方式二:使用Proxy对象,在建立连接时注入到URLConnection即可:
        
[java]  view plain  copy
  1. // 初始化proxy对象  
  2. Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyPort));  
  3.    
  4. // 创建连接  
  5. URL u = new URL(url);  
  6. URLConnection conn = u.openConnection(proxy);  

  

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

    二、实现用户密码校验

      方式一:将校验信息写入http头,将用户名密码进行base64编码之后设置Proxy-Authorization头:
 
[java]  view plain  copy
  1. String headerKey = "Proxy-Authorization";  
  2. String encoded = new String(Base64.encodeBase64((new String(proxyUser + ":" + proxyPass).getBytes())));  
  3. String headerValue = "Basic " + encoded;  
  4. conn.setRequestProperty(headerKey, headerValue);  
           
    不少资料会推荐这样的方式,但经过测试, 该方式在https的需求场景下无法正常工作
      
 
    方式二:实现Authenticator接口,并注入为全局验证器:
 
[java]  view plain  copy
  1. public static class MyAuthenticator extends Authenticator {  
  2.     String userName;  
  3.     String password;  
  4.    
  5. public MyAuthenticator (String userName, String password) {  
  6.     this.userName = userName;  
  7.     this.password = password;  
  8. }  
  9.    
  10. /** 
  11. * 当需要使用密码校验时自动触发 
  12. */  
  13. @Override  
  14. protected PasswordAuthentication getPasswordAuthentication() {  
  15.     return new PasswordAuthentication(userName, password.toCharArray());  
  16. }  
  17. }  
       
     在执行连接之前注入校验实例:
 
[java]  view plain  copy
  1. MyAuthenticator auth = new MyAuthenticator(proxyUser, proxyPass);  
  2. Authenticator.setDefault(auth);  

  

实例代码

入口类

[java]  view plain  copy
  1. /** 
  2.  * 网络代理测试 
  3.  *  
  4.  * <pre> 
  5.  * 设置代理主机及端口:系统变量(https 需同步设置) 
  6.  * 设置代理验证方式:全局代理对象 
  7.  *  
  8.  *  
  9.  * https链接错误: 
  10.  * Unable to tunnel through proxy. Proxy returns "HTTP/1.0 407 Proxy Authentication Required" 
  11.  * 使用全局代理验证解决 
  12.  *  
  13.  * </pre> 
  14.  *  
  15.  * @author tzz 
  16.  * @createDate 2015年7月23日 
  17.  *  
  18.  */  
  19. public class ProxyTest {  
  20.     private static String proxyHost = "xxx.xxxxx.com";  
  21.     private static int proxyPort = 8080;  
  22.     private static String proxyUser = "user";  
  23.     private static String proxyPass = "pass";  
  24.     public static void main(String[] args) {  
  25.         String url = "https://www.google.com/";  
  26.         String content = doProxy(url);  
  27.         System.out.println("Result :===================\n " + content);  
  28.     }  
  29.     /** 
  30.      * 通过系统变量方式实现代理 
  31.      *  
  32.      * @param url 
  33.      * @return 
  34.      */  
  35.     public static String doProxy(String url) {  
  36.         // 设置系统变量  
  37.   
  38.         System.setProperty("http.proxySet""true");  
  39.         System.setProperty("http.proxyHost", proxyHost);  
  40.         System.setProperty("http.proxyPort""" + proxyPort);  
  41.         // 针对https也开启代理  
  42.         System.setProperty("https.proxyHost", proxyHost);  
  43.         System.setProperty("https.proxyPort""" + proxyPort);  
  44.         // 设置默认校验器  
  45.         setDefaultAuthentication();  
  46.   
  47.         //开始请求  
  48.         try {  
  49.             URL u = new URL(url);  
  50.             URLConnection conn = u.openConnection();  
  51.             HttpsURLConnection httpsCon = (HttpsURLConnection) conn;  
  52.             httpsCon.setFollowRedirects(true);  
  53.   
  54.             String encoding = conn.getContentEncoding();  
  55.             if (StringUtils.isEmpty(encoding)) {  
  56.                 encoding = "UTF-8";  
  57.             }  
  58.             InputStream is = conn.getInputStream();  
  59.             String content = IOUtils.toString(is, encoding);  
  60.             return content;  
  61.         } catch (Exception e) {  
  62.             e.printStackTrace();  
  63.             return e.getMessage();  
  64.         }  
  65.     }  
  66.   
  67.     /** 
  68.      * 设置全局校验器对象 
  69.      */  
  70.     public static void setDefaultAuthentication() {  
  71.         BasicAuthenticator auth = new BasicAuthenticator(proxyUser, proxyPass);  
  72.         Authenticator.setDefault(auth);  
  73.     }  
  74. }  

校验器

[java]  view plain  copy
  1. /** 
  2.  * 实现sun.net的代理验证 
  3.  *  
  4.  * @author tzz 
  5.  * @createDate 2015年7月23日 
  6.  *  
  7.  */  
  8. public static class BasicAuthenticator extends Authenticator {  
  9.     String userName;  
  10.     String password;  
  11.     public BasicAuthenticator(String userName, String password) {  
  12.         this.userName = userName;  
  13.         this.password = password;  
  14.     }  
  15.     /** 
  16.      * Called when password authorization is needed. Subclasses should override the default implementation, which returns null. 
  17.      *  
  18.      * @return The PasswordAuthentication collected from the user, or null if none is provided. 
  19.      */  
  20.     @Override  
  21.     protected PasswordAuthentication getPasswordAuthentication() {  
  22.         //System.out.println("DEBUG === use global authentication of password");  
  23.         return new PasswordAuthentication(userName, password.toCharArray());  
  24.     }  
  25. }  

常见问题

 连接时异常
      Unable to tunnel through proxy. Proxy returns "HTTP/1.0 407 Proxy Authentication Required"
      通常是代理服务器未能读取到验证信息所致,请检查目标url是否为https连接以及全局的Authenticator类是否正确设置。

猜你喜欢

转载自blog.csdn.net/xgjianstart/article/details/78437266