開發技巧-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類是否正確設置。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM