企業防火牆通常封鎖直接外部連線,強制透過 HTTP/SOCKS Proxy 存取外網。Java 應用若不支援 Proxy,在企業環境將完全無法連線。幸運的是,Java 提供系統屬性與程式化 API 完整支援各類代理協定。
java -Dhttp.proxyHost=proxy.company.com \
-Dhttp.proxyPort=8080 \
-Dhttps.proxyHost=proxy.company.com \
-Dhttps.proxyPort=8080 \
-Dhttp.nonProxyHosts="localhost|127.0.0.1|*.internal.com" \
-jar myapp.jar
http.nonProxyHosts:繞過代理的主機清單,支援萬用字元 | 分隔。HttpURLConnection、HttpsURLConnection 自動讀取這些屬性。java -DsocksProxyHost=proxy.company.com \
-DsocksProxyPort=1080 \
-DsocksProxyVersion=5 \ # 預設 5,支援 4
-jar myapp.jar
Socket、DatagramSocket、RMI、JDBC 等 TCP 連線。系統屬性**不支援**直接指定帳密。需在程式碼設定 Authenticator:
import java.net.*;
public class ProxyAuthSetup {
public static void setup(String username, String password) {
Authenticator.setDefault(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password.toCharArray());
}
});
}
}
// 主程式啟動時呼叫
ProxyAuthSetup.setup("proxyUser", "proxyPass");
import java.net.*;
public class ProxySelectorExample {
public static void main(String[] args) throws Exception {
// 1. 定義代理
Proxy httpProxy = new Proxy(Proxy.Type.HTTP,
new InetSocketAddress("proxy.company.com", 8080));
Proxy socksProxy = new Proxy(Proxy.Type.SOCKS,
new InetSocketAddress("socks.company.com", 1080));
Proxy noProxy = Proxy.NO_PROXY; // 直連
// 2. 建立連線時指定 Proxy
URL url = new URL("https://api.external.com/data");
HttpURLConnection conn = (HttpURLConnection) url.openConnection(httpProxy);
// 3. 設定驗證(若該 Proxy 需要)
String auth = "user:pass";
String encoded = Base64.getEncoder().encodeToString(auth.getBytes());
conn.setRequestProperty("Proxy-Authorization", "Basic " + encoded);
// 4. 正常使用
try (InputStream is = conn.getInputStream()) {
// 處理回應...
}
}
}
import java.net.*;
import java.util.*;
public class CustomProxySelector extends ProxySelector {
private final ProxySelector defaultSelector;
public CustomProxySelector() {
this.defaultSelector = ProxySelector.getDefault();
}
@Override
public List select(URI uri) {
String host = uri.getHost();
// 內網直連
if (host.endsWith(".internal.com") || host.equals("localhost")) {
return Collections.singletonList(Proxy.NO_PROXY);
}
// 外網走 HTTP Proxy
return Collections.singletonList(
new Proxy(Proxy.Type.HTTP, new InetSocketAddress("proxy.company.com", 8080))
);
}
@Override
public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
// 失敗轉向預設選擇器
defaultSelector.connectFailed(uri, sa, ioe);
}
}
// 啟動時註冊
ProxySelector.setDefault(new CustomProxySelector());
| 協定/API | HTTP Proxy | HTTPS Proxy | SOCKS Proxy | 備註 |
|---|---|---|---|---|
HttpURLConnection | ✅ 自動 | ✅ 自動 (CONNECT 隧道) | ✅ 程式化指定 | 最常用 |
HttpsURLConnection | ✅ 自動 | ✅ 自動 | ✅ 程式化指定 | SSL 握手在隧道內 |
Socket / ServerSocket | ❌ | ❌ | ✅ 系統屬性/程式化 | 需 SOCKS |
| RMI / JMX | ❌ | ❌ | ✅ -DsocksProxyHost | 建議用 SOCKS |
| JDBC (資料庫驅動) | 視驅動而定 | 視驅動而定 | 視驅動而定 | 多數支援 SOCKS 或自有 Proxy 參數 |
| FTP (URL "ftp://") | ✅ ftp.proxyHost | ❌ | ❌ | 舊 API,建議改用 Apache Commons Net |
CONNECT host:443 HTTP/1.1 建立 TLS 隧道,Java 自動處理,**無法攔截解密內容**(除非中間人憑證)。Basic base64(user:pass),注意 Basic 關鍵字與 base64 之間有空格。http.proxyPort 預設 80,https.proxyPort 預設 443,企業 Proxy 常用 8080、3128、8888,**必須明確指定**。Dockerfile 或 docker run -e 傳入 HTTP_PROXY/HTTPS_PROXY 環境變數,啟動腳本轉為 -D 參數。telnet proxy.company.com 8080 或 curl -x proxy.company.com:8080 https://httpbin.org/ip 確認 Proxy 本身可達。public final class ProxyUtils {
private ProxyUtils() {}
public static HttpURLConnection openConnection(URL url, ProxyConfig config) throws IOException {
Proxy proxy = config.toProxy();
HttpURLConnection conn = (HttpURLConnection) url.openConnection(proxy);
if (config.requiresAuth()) {
String auth = config.getUsername() + ":" + config.getPassword();
String encoded = Base64.getEncoder().encodeToString(auth.getBytes());
conn.setRequestProperty("Proxy-Authorization", "Basic " + encoded);
}
return conn;
}
public static class ProxyConfig {
private final Proxy.Type type;
private final String host;
private final int port;
private final String username;
private final String password;
public ProxyConfig(Proxy.Type type, String host, int port) {
this(type, host, port, null, null);
}
public ProxyConfig(Proxy.Type type, String host, int port, String username, String password) {
this.type = type; this.host = host; this.port = port;
this.username = username; this.password = password;
}
public Proxy toProxy() {
return new Proxy(type, new InetSocketAddress(host, port));
}
public boolean requiresAuth() { return username != null && password != null; }
public String getUsername() { return username; }
public String getPassword() { return password; }
}
}
總結:簡單部署用 -Dhttp.proxyHost 啟動參數;需動態切換、多代理路由用 ProxySelector;精細控制單一連線用 url.openConnection(proxy)。驗證統一用 Authenticator 或手動加 Proxy-Authorization 標頭。記得處理 http.nonProxyHosts 避免內網流量繞道。