絕大多數企業為安全考量,內部網路不直接連通公網,需透過 Proxy Server (代理伺服器) 轉發 HTTP/HTTPS/FTP 流量。Java 應用若未正確配置 Proxy,將拋出 ConnectException: Connection timed out 或 UnknownHostException。本文彙整 JDK 內建系統屬性配置、認證處理、SOCKS 代理、各協定差異及版本相容性陷阱,助你一次解決所有代理問題。
在建立任何 URL、HttpURLConnection、Socket 連線之前,設定下列系統屬性:
System.setProperty("http.proxyHost", "proxy.mycompany.com");
System.setProperty("http.proxyPort", "8080");
System.setProperty("https.proxyHost", "proxy.mycompany.com");
System.setProperty("https.proxyPort", "8080");
// 選項:繞過代理的主機/網域 (JDK 1.5+)
System.setProperty("http.nonProxyHosts", "localhost|127.0.0.1|*.mycompany.com");
注意:
proxySet、proxyHost、proxyPort為舊版 (JDK 1.1/1.2) 寫法,現代 JDK 僅建議使用http.proxyHost等標準屬性。舊屬性可能在某些版本失效。
若代理需帳密驗證,需在 HttpURLConnection 請求頭加入 Proxy-Authorization:
String username = "user";
String password = "pass";
String auth = username + ":" + password;
String encoded = Base64.getEncoder().encodeToString(auth.getBytes(StandardCharsets.ISO_8859_1));
// 或使用 JDK 內建 sun.misc.BASE64Encoder (非標準 API)
// String encoded = new sun.misc.BASE64Encoder().encode(auth.getBytes());
URL url = new URL("https://example.com");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestProperty("Proxy-Authorization", "Basic " + encoded);
關鍵點:Header 值必須以
Basic開頭 (區分大小寫),後接 Base64 編碼字串。編碼時建議用 ISO-8859-1 避免中文亂碼。
System.setProperty("ftp.proxyHost", "proxy.mycompany.com");
System.setProperty("ftp.proxyPort", "2121");
// JDK 無 ftp.nonProxyHosts,若需直連需自行判斷或用 SOCKS
使用方式同 HTTP:new URL("ftp://ftp.example.com/file.txt").openStream()。
SOCKS 在 TCP 層轉發,對所有 TCP Socket 生效 (含 HTTP、FTP、自訂協定):
System.setProperty("socksProxyHost", "socks.mycompany.com");
System.setProperty("socksProxyPort", "1080"); // 預設 1080
// SOCKS5 支援認證需搭配 Authenticator
Authenticator.setDefault(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("socksUser", "socksPass".toCharArray());
}
});
優先序:若同時設定
http.proxyHost與socksProxyHost,HTTP/HTTPS 走 HTTP Proxy;其他 TCP 走 SOCKS。若需強制 HTTP 走 SOCKS,可不設 HTTP Proxy 僅設 SOCKS。
不想汙染全域系統屬性?針對單一連線指定 Proxy:
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("proxy.host", 8080));
URL url = new URL("https://example.com");
HttpURLConnection conn = (HttpURLConnection) url.openConnection(proxy);
// 或 Socket
Socket socket = new Socket(proxy);
socket.connect(new InetSocketAddress("target.host", 80));
此法優先於系統屬性,適合多租戶、動態切換代理場景。
| 問題 | 原因 | 解決方案 |
|---|---|---|
| JDK 1.1.7 / WebSphere 3.0 系統屬性無效 | 舊版實作缺陷 | 使用 new URL(protocol, host, port, file, handler) 建構子傳入 Proxy 主機/埠口,或升級 JDK |
| HTTPS 走代理但憑證驗證失敗 | 代理執行 SSL 攔截 (MITM) | 將代理 CA 憑證匯入 JVM truststore (cacerts),或實作信任所有憑證的 TrustManager (僅測試用) |
| 非標準埠口 HTTP 連線被代理攔截 | 代理僅允許 80/443/8080 | 聯繫網管開放埠口,或改用 SOCKS/SSH Tunnel |
| Proxy-Authorization 憑證包含特殊字元 | Base64 編碼前未正確處理 | 嚴格遵循 RFC 7617:`user:pass` 用 ISO-8859-1 編碼後 Base64 |
ProxyConfig 類,集中管理 HTTP/HTTPS/FTP/SOCKS 設定與認證。https://www.google.com) 發送 HEAD 請求驗證代理通暢。Java 代理支援機制成熟但細節多。核心原則:盡早設定系統屬性或使用 Proxy 類別、正確處理 Basic Auth Header、區分 HTTP/SOCKS 適用場景、注意版本相容性。掌握這些,無論是爬蟲、微服務呼叫、Maven/Gradle 依賴下載,都能在企業網路環境暢行無阻。