需求描述
在正常的项目开发需求中,连接远程服务器的场景一般有二:
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);
-
-
- System.setProperty("https.proxyHost", proxyHost);
- System.setProperty("https.proxyPort", "" + proxyPort);
方式二:使用Proxy对象,在建立连接时注入到URLConnection即可:
-
- 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);
实例代码
入口类
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 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);
- }
-
-
-
-
-
-
- public static String doProxy(String url) {
-
-
- System.setProperty("http.proxySet", "true");
- System.setProperty("http.proxyHost", proxyHost);
- System.setProperty("http.proxyPort", "" + proxyPort);
-
- 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);
- }
- }
校验器
-
-
-
-
-
-
-
- public static class BasicAuthenticator extends Authenticator {
- String userName;
- String password;
- public BasicAuthenticator(String userName, String password) {
- this.userName = userName;
- this.password = password;
- }
-
-
-
-
-
- @Override
- protected PasswordAuthentication getPasswordAuthentication() {
-
- return new PasswordAuthentication(userName, password.toCharArray());
- }
- }
常见问题
连接时异常
Unable to tunnel through proxy. Proxy returns "HTTP/1.0 407 Proxy Authentication Required"
通常是代理服务器未能读取到验证信息所致,请检查目标url是否为https连接以及全局的Authenticator类是否正确设置。