开发技巧-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代理及端口,如下:
 
1
2
3
4
5
6
7
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即可:
        
1
2
3
4
5
6
// 初始化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头:
 
1
2
3
4
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接口,并注入为全局验证器:
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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());
}
}
       
     在执行连接之前注入校验实例:
 
1
2
MyAuthenticator auth = new MyAuthenticator(proxyUser, proxyPass);
Authenticator.setDefault(auth);

  

实例代码

入口类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
/**
 * 网络代理测试
 *
 * <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);
    }
}

校验器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
 * 实现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类是否正确设置。

 

posted @   美码师  阅读(26601)  评论(3编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
点击右上角即可分享
微信分享提示