1. 程式人生 > >開發技巧-Java通過HttpProxy實現穿越

開發技巧-Java通過HttpProxy實現穿越

efault collected ddr apach pac 建立 create set 設置

需求描寫敘述

在正常的項目開發需求中。連接遠程server的場景一般有二: 1 自家實現的httpserver,api接口都已經約定好。 2 開發平臺服務。通常如新浪、百度雲等平臺提供的restful接口。 以上的兩種場景通過原生的URLConnection或是apache提供的httpclient工具包都能夠方便的實現調用。

然而,第三種場景是須要連接國外的開放服務,如google、twitter、tumblr等開放API接口。

在偉大的gfw關懷下,我們被告知不要隨便和陌生人說話... 好吧,接下來讓我們開始實現基於proxy的穿越吧!

準備工作

1 http代理server 建議花點銀子買個穩定的VPN,帶http代理的那種。 2 外網訪問測試 能夠用chrome switchyOmega插件測試一把。不行直接設置IE系統代理 準備完成。能夠開始開發了

設計分析

代理連接實現的關鍵步驟:

一、設置代理server地址port

方式一:Java支持以System.setProperty的方式設置http代理及port,例如以下:
    
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的方式則不須要動裏面的代碼,綠色輕便。

二、實現用戶password校驗

方式一:將校驗信息寫入http頭,將usernamepassword進行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>
 * 設置代理主機及port:系統變量(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" 一般是代理server未能讀取到驗證信息所致,請檢查目標url是否為https連接以及全局的Authenticator類是否正確設置。

開發技巧-Java通過HttpProxy實現穿越