需求描述

    在正常的项目开发需求中,连接远程服务器的场景一般有二:
    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类是否正确设置。



开发技巧-Java通过HttpProxy实现穿越的更多相关文章

  1. Java 8的五大开发技巧

    转载:http://geek.csdn.net/news/detail/94219 在Java 9发布之前,我们来分享一些Java 8开发技巧,本文翻译自JetBrains高级开发主管Trisha G ...

  2. WebApp开发技巧大全 看了就明白了

    [转载]阅读原文 自Iphone和Android这两个牛逼的手机操作系统发布以来,在互联网界从此就多了一个新的名词-WebApp(意为基于WEB形式的应用程 序,运行在高端的移动终端设备).开发者们都 ...

  3. Android开发技巧——大图裁剪

    本篇内容是接上篇<Android开发技巧--定制仿微信图片裁剪控件> 的,先简单介绍对上篇所封装的裁剪控件的使用,再详细说明如何使用它进行大图裁剪,包括对旋转图片的裁剪. 裁剪控件的简单使 ...

  4. Android开发技巧——使用PopupWindow实现弹出菜单

    在本文当中,我将会与大家分享一个封装了PopupWindow实现弹出菜单的类,并说明它的实现与使用. 因对界面的需求,android原生的弹出菜单已不能满足我们的需求,自定义菜单成了我们的唯一选择,在 ...

  5. Android开发技巧——实现可复用的ActionSheet菜单

    在上一篇<Android开发技巧--使用Dialog实现仿QQ的ActionSheet菜单>中,讲了这种菜单的实现过程,接下来将把它改成一个可复用的控件库. 本文原创,转载请注明出处: h ...

  6. Android开发技巧——自定义控件之使用style

    Android开发技巧--自定义控件之使用style 回顾 在上一篇<Android开发技巧--自定义控件之自定义属性>中,我讲到了如何定义属性以及在自定义控件中获取这些属性的值,也提到了 ...

  7. Android开发技巧——自定义控件之自定义属性

    Android开发技巧--自定义控件之自定义属性 掌握自定义控件是很重要的,因为通过自定义控件,能够:解决UI问题,优化布局性能,简化布局代码. 上一篇讲了如何通过xml把几个控件组织起来,并继承某个 ...

  8. Android开发技巧——自定义控件之组合控件

    Android开发技巧--自定义控件之组合控件 我准备在接下来一段时间,写一系列有关Android自定义控件的博客,包括如何进行各种自定义,并分享一下我所知道的其中的技巧,注意点等. 还是那句老话,尽 ...

  9. ES6 Javascript 实用开发技巧

    ES6 实用开发技巧 定义变量/常量 ES6 中新增加了 let 和 const 两个命令,let 用于定义变量,const 用于定义常量 两个命令与原有的 var 命令所不同的地方在于,let, c ...

随机推荐

  1. 理解RxJava线程模型

    RxJava作为目前一款超火的框架,它便捷的线程切换一直被人们津津乐道,本文从源码的角度,来对RxJava的线程模型做一次深入理解.(注:本文的多处代码都并非原本的RxJava的源码,而是用来说明逻辑 ...

  2. CSS 3中边框怎么用

    (1)设置边框图片的来源 图片边框默认只在四个顶点显示 none: 无背景图片; border-image-source: url('borderImage.png'); (2)边框图片的分割 将图片 ...

  3. redis.conf 配置详解 (转)

    # Redis 配置文件 # 当配置中需要配置内存大小时,可以使用 1k, 5GB, 4M 等类似的格式,其转换方式如下(不区分大小写)## 1k => 1000 bytes# 1kb => ...

  4. 【转】 CPU大小端

    大端模式,是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中:小端模式,是指数据的低位保存在内存的低地址中,而数据的高位保存在内存的高地址中. 为什么会有大小端模式之分呢?这是因为 ...

  5. 基础知识复习(二)——stdafx.h 头文件及x&(x-1)运算

    今天好久没写过C++程序了,使用VS2013 新建空的控制台程序,结果自动生成了头文件和main 方法. 就了解了stdafx.h头文件的含义及用法. stdafx:standard Applicat ...

  6. windows docker测试二 下载container

    安装dockertoolbox,提供了一个docker的界面工具 Kitematic 和字符终端: Docker Quickstart Terminal (这里安装的Kitematic 是Alpha版 ...

  7. iOS开发网络篇—网络编程基础

    iOS开发网络篇—网络编程基础 一.为什么要学习网络编程 1.简单说明 在移动互联网时代,移动应用的特征有: (1)几乎所有应用都需要用到网络,比如QQ.微博.网易新闻.优酷.百度地图 (2)只有通过 ...

  8. Android Activity的onSaveInstanceState() 和 onRestoreInstanceState()方法:

    Android Activity的onSaveInstanceState() 和 onRestoreInstanceState()方法: 1. 基本作用: Activity的 onSaveInstan ...

  9. Quartz Spring与Spring Task总结

    Spring对Quartz作了一个封装,同时,Spring自己也提供了一个任务定时器(spring-task),现把它总结一下.    对于Quartz,我们使用的时候主要是注重两个方面,一个是定时任 ...

  10. Android TextView里显示两种颜色

    今天介绍一个小技巧,在Android的TextView里设置两种颜色,直接上代码: TextView TV = (TextView)findViewById(R.id.mytextview01); S ...