
正文
开发技巧-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类是否正确设置。
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类是否正确设置。
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的方式则不需要动里面的代码,绿色轻便。
String headerKey = "Proxy-Authorization";
String encoded = new String(Base64.encodeBase64((new String(proxyUser + ":" + proxyPass).getBytes())));
String headerValue = "Basic " + encoded;
conn.setRequestProperty(headerKey, headerValue);
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());
}
}







