Unternehmens-Java-Anwendungen greifen regelmäßig auf externe APIs zu, parsen Daten oder arbeiten in isolierten Unternehmensnetzwerken — und in jedem dieser Fälle stellt sich früher oder später die Frage, wie der Datenverkehr über einen Proxy-Server geleitet werden kann. Die Konfiguration eines Proxys in Java scheint eine einfache Aufgabe zu sein, bis man feststellt, dass RestTemplate, OkHttp und WebClient sich unterschiedlich verhalten und die JVM-Systemeigenschaften nicht überall funktionieren. In diesem Leitfaden finden Sie konkrete Codebeispiele, Erklärungen zu Fallstricken und Empfehlungen zur Auswahl des Proxy-Typs für verschiedene Aufgaben.
Warum benötigen Java-Anwendungen Proxys?
Java bleibt eine der Hauptsprachen für die Unternehmensentwicklung. Spring Boot-Dienste, Microservice-Architekturen, Batch-Jobs, ETL-Pipelines — all dies führt regelmäßig HTTP-Anfragen an die Außenwelt aus. In jedem dieser Szenarien löst ein Proxy eine spezifische Aufgabe:
- Unternehmensnetzwerke mit obligatorischem Proxy-Gateway. In vielen Unternehmen wird der gesamte ausgehende Datenverkehr über einen Unternehmensproxy (Squid, Zscaler, BlueCoat) geleitet. Ohne dessen Konfiguration kann die Anwendung einfach nicht ins Internet gelangen.
- Datenparsing und -sammlung. Dienste, die Daten von externen Websites sammeln (Preise, Zitate, Nachrichten), verwenden Proxys, um Rate-Limits und IP-Sperren zu umgehen.
- Geo-Testing. QA-Teams überprüfen, wie sich APIs oder Websites aus verschiedenen Regionen verhalten — Proxys ermöglichen es, Anfragen aus dem gewünschten Land zu emulieren.
- Umgehung von Einschränkungen auf der Seite der Ziel-API. Einige externe APIs haben Limits für die Anzahl von Anfragen von einer IP. Die Rotation von Proxys ermöglicht es, die Last zu verteilen.
- Sicherheit und Anonymisierung. Das Verbergen der echten IP der Anwendung beim Zugriff auf externe Dienste ist eine gängige Praxis für sensible Integrationen.
Je nach Aufgabe unterscheiden sich die Ansätze zur Proxy-Konfiguration in Java erheblich. Globale JVM-Systemeigenschaften sind die einfachste Option, funktionieren jedoch nicht mit einigen HTTP-Clients. OkHttp und WebClient erfordern eine explizite Konfiguration. Lassen Sie uns jeden Fall der Reihe nach betrachten.
Globale Konfiguration über JVM-Systemeigenschaften
Der schnellste Weg, den gesamten HTTP-Datenverkehr einer Java-Anwendung über einen Proxy zu leiten, besteht darin, die JVM-Systemeigenschaften zu verwenden. Dies funktioniert für die Standard-HttpURLConnection und viele Bibliotheken, die sie im Hintergrund verwenden.
Der Proxy kann auf verschiedene Arten festgelegt werden:
1. Über JVM-Argumente beim Start:
java -Dhttp.proxyHost=proxy.example.com \
-Dhttp.proxyPort=8080 \
-Dhttps.proxyHost=proxy.example.com \
-Dhttps.proxyPort=8080 \
-Dhttp.nonProxyHosts="localhost|127.0.0.1|*.internal.corp" \
-jar myapp.jar
2. Über den Anwendungscode (z. B. in der Methode main oder in @PostConstruct):
System.setProperty("http.proxyHost", "proxy.example.com");
System.setProperty("http.proxyPort", "8080");
System.setProperty("https.proxyHost", "proxy.example.com");
System.setProperty("https.proxyPort", "8080");
// Ausnahmen — Hosts, die nicht über den Proxy geleitet werden sollen
System.setProperty("http.nonProxyHosts", "localhost|127.0.0.1|*.internal.corp");
3. Über die Datei application.properties in Spring Boot (mit anschließender Anwendung über @PostConstruct):
# application.properties proxy.host=proxy.example.com proxy.port=8080 proxy.username=user proxy.password=secret
⚠️ Wichtig zu wissen
JVM-Systemeigenschaften funktionieren nur für HttpURLConnection. OkHttp, Apache HttpClient und Reactor Netty (WebClient) ignorieren diese Eigenschaften — sie benötigen eine explizite Konfiguration, die unten beschrieben wird.
Wenn der Proxy eine Authentifizierung erfordert, verwenden Sie Authenticator:
Authenticator.setDefault(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
if (getRequestorType() == RequestorType.PROXY) {
return new PasswordAuthentication("username", "password".toCharArray());
}
return null;
}
});
Proxys in Spring Boot: RestTemplate und Apache HttpClient
RestTemplate ist der standardmäßige synchrone HTTP-Client in Spring. Standardmäßig verwendet er SimpleClientHttpRequestFactory, die auf HttpURLConnection basiert und die JVM-Systemeigenschaften liest. Für eine flexiblere Konfiguration (Timeouts, Verbindungs-Pooling, Proxys mit Authentifizierung) wird empfohlen, Apache HttpClient als Backend zu verwenden.
Abhängigkeit in Maven:
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.3.1</version>
</dependency>
Konfiguration von RestTemplate mit Proxy über Apache HttpClient 5:
@Configuration
public class RestTemplateConfig {
@Value("${proxy.host}")
private String proxyHost;
@Value("${proxy.port}")
private int proxyPort;
@Value("${proxy.username:}")
private String proxyUsername;
@Value("${proxy.password:}")
private String proxyPassword;
@Bean
public RestTemplate restTemplate() {
HttpHost proxy = new HttpHost("http", proxyHost, proxyPort);
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
if (!proxyUsername.isEmpty()) {
credentialsProvider.setCredentials(
new AuthScope(proxyHost, proxyPort),
new UsernamePasswordCredentials(proxyUsername, proxyPassword.toCharArray())
);
}
CloseableHttpClient httpClient = HttpClients.custom()
.setProxy(proxy)
.setDefaultCredentialsProvider(credentialsProvider)
.build();
HttpComponentsClientHttpRequestFactory factory =
new HttpComponentsClientHttpRequestFactory(httpClient);
factory.setConnectTimeout(5000);
factory.setReadTimeout(10000);
return new RestTemplate(factory);
}
}
Eine solche Konfiguration ermöglicht es, die Proxy-Parameter in application.properties oder Umgebungsvariablen auszulagern, was für Prod/Staging/Dev-Umgebungen mit unterschiedlichen Proxys entscheidend ist.
Wenn ein Teil der Anfragen über den Proxy und ein Teil direkt gehen soll, erstellen Sie zwei separate Beans RestTemplate mit unterschiedlichen Namen und verwenden Sie @Qualifier für die Injektion des benötigten.
Proxys in Spring WebClient (reaktiver Stack)
WebClient ist der reaktive HTTP-Client von Spring, der auf Reactor Netty basiert. Er liest die JVM-Systemeigenschaften für Proxys nicht — die Konfiguration erfolgt über ProxyProvider bei der Erstellung des Clients.
Abhängigkeit (Teil von spring-boot-starter-webflux):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
Konfiguration von WebClient mit HTTP-Proxy:
@Configuration
public class WebClientConfig {
@Bean
public WebClient webClient() {
HttpClient httpClient = HttpClient.create()
.proxy(proxy -> proxy
.type(ProxyProvider.Proxy.HTTP)
.host("proxy.example.com")
.port(8080)
.username("user")
.password(user -> "password")
);
ClientHttpConnector connector =
new ReactorClientHttpConnector(httpClient);
return WebClient.builder()
.clientConnector(connector)
.build();
}
}
Für SOCKS5-Proxys in WebClient:
HttpClient httpClient = HttpClient.create()
.proxy(proxy -> proxy
.type(ProxyProvider.Proxy.SOCKS5)
.host("socks5.proxy.example.com")
.port(1080)
.username("user")
.password(user -> "password")
);
💡 Konfigurationstipp
In reaktiven Anwendungen müssen oft mehrere Instanzen von WebClient mit unterschiedlichen Proxys für verschiedene externe Dienste erstellt werden. Verwenden Sie WebClient.Builder als Prototyp-Bean und überschreiben Sie die Einstellungen über .mutate().
Proxy-Konfiguration in OkHttp
OkHttp ist eine beliebte HTTP-Bibliothek, die von vielen Java- und Android-Anwendungen sowie einigen Spring-Integrationen (z. B. Feign mit OkHttp-Backend) verwendet wird. Sie hat ihren eigenen Mechanismus zur Proxy-Konfiguration und liest ebenfalls nicht die JVM-Systemeigenschaften.
Grundlegende Konfiguration von HTTP/HTTPS-Proxys in OkHttp:
import okhttp3.*;
import java.net.InetSocketAddress;
import java.net.Proxy;
// Proxy erstellen
Proxy proxy = new Proxy(
Proxy.Type.HTTP,
new InetSocketAddress("proxy.example.com", 8080)
);
// Proxy-Authentifizierung
Authenticator proxyAuthenticator = (route, response) -> {
String credential = Credentials.basic("username", "password");
return response.request().newBuilder()
.header("Proxy-Authorization", credential)
.build();
};
// Client erstellen
OkHttpClient client = new OkHttpClient.Builder()
.proxy(proxy)
.proxyAuthenticator(proxyAuthenticator)
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build();
// Beispielanfrage
Request request = new Request.Builder()
.url("https://api.example.com/data")
.build();
try (Response response = client.newCall(request).execute()) {
System.out.println(response.body().string());
}
Integration von OkHttp mit Spring Boot über Bean:
@Configuration
@ConditionalOnProperty(name = "proxy.enabled", havingValue = "true")
public class OkHttpConfig {
@Value("${proxy.host}")
private String proxyHost;
@Value("${proxy.port}")
private int proxyPort;
@Bean
public OkHttpClient okHttpClient() {
Proxy proxy = new Proxy(
Proxy.Type.HTTP,
new InetSocketAddress(proxyHost, proxyPort)
);
return new OkHttpClient.Builder()
.proxy(proxy)
.connectionPool(new ConnectionPool(10, 5, TimeUnit.MINUTES))
.build();
}
}
Beachten Sie @ConditionalOnProperty — dies ermöglicht es, den Proxy nur in den benötigten Umgebungen über die Konfiguration zu aktivieren, ohne den Code zu ändern.
Wenn Sie OkHttp zusammen mit residential Proxys zum Parsen von Daten verwenden, ist es wichtig, den Verbindungspool richtig zu konfigurieren. Residential Proxys haben oft eine höhere Latenz im Vergleich zu Rechenzentrums-Proxys, daher sollten Sie die Timeouts erhöhen und die Poolgröße verringern, um die verfügbaren Verbindungen nicht zu erschöpfen.
SOCKS5-Proxys in Java: Besonderheiten und Beispiele
SOCKS5 ist ein niedrigschwelliger Protokoll im Vergleich zu HTTP-Proxys. Es arbeitet auf der Transportschicht und unterstützt alle Protokolle (HTTP, HTTPS, FTP usw.) sowie DNS-Resolution auf der Proxy-Seite. Für Java-Anwendungen ist SOCKS5 besonders nützlich, wenn nicht nur HTTP, sondern auch andere TCP-Verbindungen proxiert werden müssen.
SOCKS5 über JVM-Systemeigenschaften:
System.setProperty("socksProxyHost", "socks5.proxy.example.com");
System.setProperty("socksProxyPort", "1080");
System.setProperty("java.net.socks.username", "user");
System.setProperty("java.net.socks.password", "password");
SOCKS5 in OkHttp (über java.net.Proxy):
Proxy socks5Proxy = new Proxy(
Proxy.Type.SOCKS,
new InetSocketAddress("socks5.proxy.example.com", 1080)
);
OkHttpClient client = new OkHttpClient.Builder()
.proxy(socks5Proxy)
.build();
Wichtige Besonderheit von SOCKS5 und DNS: Standardmäßig löst Java DNS lokal auf und überträgt bereits die IP-Adresse über den Proxy. Dies kann die echte DNS-Anfrage offenbaren. Um sicherzustellen, dass die DNS-Resolution ebenfalls über den Proxy erfolgt, verwenden Sie den folgenden Ansatz mit OkHttp:
// Benutzerdefinierter DNS, der die Resolution über SOCKS5 erzwingt
OkHttpClient client = new OkHttpClient.Builder()
.proxy(socks5Proxy)
.dns(hostname -> {
// Geben Sie den nicht aufgelösten Host zurück — OkHttp übergibt ihn an den Proxy
return Collections.singletonList(InetAddress.getByName(hostname));
})
.build();
Wann SOCKS5 vs. HTTP-Proxy wählen
| Kriterium | HTTP-Proxy | SOCKS5-Proxy |
|---|---|---|
| Protokolle | HTTP/HTTPS | Alle TCP/UDP |
| DNS über Proxy | Hängt von der Implementierung ab | Wird unterstützt |
| Kompatibilität mit Java | Nativ | Nativ (java.net.Proxy.Type.SOCKS) |
| Verwendung | Unternehmensgateways, Web-Scraping | Anonymisierung, nicht standardisierte Protokolle |
Proxy-Rotation in Java-Anwendungen
Wenn eine Anwendung eine große Anzahl von Anfragen an eine Ressource sendet, führt die Verwendung einer einzigen Proxy-IP schnell zu einer Sperrung. Die Rotation von Proxys — das Wechseln zwischen verschiedenen IP-Adressen — löst dieses Problem. In Java kann dies auf verschiedene Arten implementiert werden.
Option 1: Rotation über einen rotating proxy endpoint
Der einfachste Ansatz ist die Verwendung eines Proxy-Anbieters mit einem einzigen Endpoint, der die IP bei jeder Anfrage oder in einem festgelegten Intervall automatisch ändert. Sie geben einen Host und Port an, und die Rotation erfolgt auf der Seite des Anbieters. Es ist kein Code für die Rotation erforderlich.
Option 2: Rotation auf Anwendungsebene über eine Liste von Proxys
@Component
public class ProxyRotator {
private final List<ProxyConfig> proxies;
private final AtomicInteger counter = new AtomicInteger(0);
public ProxyRotator(@Value("${proxy.list}") List<String> proxyList) {
this.proxies = proxyList.stream()
.map(this::parseProxy)
.collect(Collectors.toList());
}
public OkHttpClient getClientWithNextProxy() {
int index = counter.getAndIncrement() % proxies.size();
ProxyConfig config = proxies.get(index);
Proxy proxy = new Proxy(
Proxy.Type.HTTP,
new InetSocketAddress(config.getHost(), config.getPort())
);
return new OkHttpClient.Builder()
.proxy(proxy)
.build();
}
private ProxyConfig parseProxy(String proxyStr) {
// Parsen Sie die Zeichenfolge im Format "host:port" oder "user:pass@host:port"
String[] parts = proxyStr.split(":");
return new ProxyConfig(parts[0], Integer.parseInt(parts[1]));
}
}
Option 3: Rotation über ProxySelector
Java bietet einen integrierten Mechanismus ProxySelector, der es ermöglicht, Proxys dynamisch basierend auf der Ziel-URL auszuwählen. Dies funktioniert mit HttpURLConnection und Apache HttpClient:
ProxySelector.setDefault(new ProxySelector() {
private final List<Proxy> proxyPool = Arrays.asList(
new Proxy(Proxy.Type.HTTP, new InetSocketAddress("proxy1.example.com", 8080)),
new Proxy(Proxy.Type.HTTP, new InetSocketAddress("proxy2.example.com", 8080)),
new Proxy(Proxy.Type.HTTP, new InetSocketAddress("proxy3.example.com", 8080))
);
private final AtomicInteger idx = new AtomicInteger(0);
@Override
public List<Proxy> select(URI uri) {
// Logik zur Auswahl basierend auf der Domain kann hinzugefügt werden
int i = idx.getAndIncrement() % proxyPool.size();
return Collections.singletonList(proxyPool.get(i));
}
@Override
public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
// Logik zur Behandlung eines nicht verfügbaren Proxys
log.warn("Proxy failed: {}", sa);
}
});
Welchen Proxy-Typ für Java-Aufgaben wählen?
Die Wahl des Proxy-Typs hängt von der spezifischen Aufgabe ab. Für Unternehmens-Java-Anwendungen werden in der Regel drei Haupttypen in Betracht gezogen:
| Proxy-Typ | Geschwindigkeit | Anonymität | Am besten geeignet für |
|---|---|---|---|
| Rechenzentrums-Proxys | Hoch | Mittel | Scraping öffentlicher Daten, API-Tests, hohe Last |
| Residential Proxys | Mittel | Hoch | Scraping geschützter Websites, Geo-Testing, Umgehung von Sperren |
| Mobile Proxys | Mittel | Maximal | Integrationen mit mobilen APIs, Dienste mit strengen Anti-Fraud-Schutz |
Praktische Empfehlungen für die Typen von Aufgaben:
- Unternehmensgateway (Squid, Zscaler): Der Proxy-Typ wird von der IT-Abteilung festgelegt. Verwenden Sie HTTP-Proxys mit Authentifizierung über Apache HttpClient oder JVM-Systemeigenschaften.
- Scraping öffentlicher Daten (Nachrichten, Zitate, offene APIs): Rechenzentrums-Proxys sind die optimale Wahl im Hinblick auf Geschwindigkeit und Kosten.
- Scraping von E-Commerce (Wildberries, Ozon, Amazon): Residential Proxys mit Rotation. Diese Plattformen blockieren aktiv Rechenzentrums-IP.
- Geo-Testing (Überprüfung der Lokalisierung, Preise nach Regionen): Residential Proxys mit Auswahl von Land/Stadt.
- Integration mit mobilen Diensten und Anwendungen: Mobile Proxys — IPs aus realen Mobilfunknetzen simulieren am besten echte Benutzer.
Häufige Fehler bei der Proxy-Konfiguration in Java und wie man sie behebt
Selbst erfahrene Entwickler stoßen bei der Proxy-Konfiguration in Java auf dieselben Probleme. Lassen Sie uns die häufigsten betrachten.
1. Proxy ist konfiguriert, aber der Datenverkehr geht trotzdem direkt
Ursache: Sie haben die JVM-Systemeigenschaften festgelegt, verwenden jedoch OkHttp oder Reactor Netty (WebClient), die diese ignorieren.
Lösung: Konfigurieren Sie den Proxy explizit in der Konfiguration des spezifischen HTTP-Clients, wie in den obigen Abschnitten gezeigt.
2. javax.net.ssl.SSLHandshakeException bei HTTPS über Proxy
Ursache: Der Unternehmensproxy führt eine SSL-Inspektion (MITM) durch, indem er das Zertifikat ersetzt. Java vertraut dem Unternehmens-CA nicht.
Lösung: Importieren Sie das Unternehmenszertifikat in den Java Truststore:
keytool -import -trustcacerts \ -alias corporate-ca \ -file corporate-ca.crt \ -keystore $JAVA_HOME/lib/security/cacerts \ -storepass changeit
3. java.net.ConnectException: Verbindung abgelehnt
Ursachen: Falscher Host oder Port des Proxys; der Proxy-Server ist nicht verfügbar; Firewall blockiert die Verbindung.
Diagnose:
# Überprüfen Sie die Verfügbarkeit des Proxys curl -x http://proxy.example.com:8080 https://httpbin.org/ip # Oder über telnet telnet proxy.example.com 8080
4. Proxy funktioniert für HTTP, aber nicht für HTTPS
Ursache: Nur die Eigenschaften http.proxyHost/Port wurden festgelegt, aber nicht https.proxyHost/Port.
Lösung: Legen Sie beide Eigenschaftensätze fest. Für HTTPS über einen HTTP-Proxy wird die CONNECT-Tunneling-Methode verwendet — stellen Sie sicher, dass der Proxy dies unterstützt.
5. 407 Proxy-Authentifizierung erforderlich
Ursache: Der Proxy erfordert eine Authentifizierung, die jedoch nicht konfiguriert ist.
Lösung: Fügen Sie einen Authenticator für die JVM-Systemeigenschaften hinzu oder konfigurieren Sie einen CredentialsProvider für Apache HttpClient / proxyAuthenticator für OkHttp (Beispiele oben).
6. Speicherleck bei der Proxy-Rotation
Ursache: Bei jeder Anfrage wird eine neue Instanz von OkHttpClient erstellt. Jeder Client hält seinen eigenen Pool von Threads und Verbindungen.
Lösung: Wiederverwenden Sie Clients. Erstellen Sie einen Pool aus mehreren Clients (einen pro Proxy) und rotieren Sie diese, anstatt neue zu erstellen.
7. Aktivierung des Debug-Loggings zur Diagnose
# application.properties — aktivieren Sie Logs zur Diagnose des Proxys logging.level.org.apache.http=DEBUG logging.level.org.apache.http.wire=DEBUG logging.level.reactor.netty.http.client=DEBUG # Für die JVM — Systemeigenschaft -Djava.net.debug=all
Fazit
Die Konfiguration von Proxys in Java-Anwendungen erfordert ein Verständnis dafür, welcher HTTP-Client im Hintergrund verwendet wird. JVM-Systemeigenschaften sind ein schneller Start für HttpURLConnection, aber OkHttp, WebClient und Apache HttpClient erfordern eine explizite Konfiguration. Für Unternehmensanwendungen wird empfohlen, die Proxy-Parameter in application.properties auszulagern und Spring-Profile für verschiedene Umgebungen zu verwenden. Die Rotation von Proxys sollte besser einem Anbieter mit rotating endpoint überlassen werden — das ist einfacher und zuverlässiger, als es selbst zu implementieren.
Wenn Ihre Java-Anwendung Daten von geschützten Plattformen parst oder eine hohe Anonymität bei externen Anfragen erfordert, empfehlen wir, residential Proxys in Betracht zu ziehen — sie haben echte IPs von Heimnutzern, was die Wahrscheinlichkeit von Sperren durch Anti-Fraud-Systeme erheblich verringert. Für hochbelastetes Scraping von offenen Daten sind Rechenzentrums-Proxys optimal — sie bieten maximale Geschwindigkeit bei geringeren Kosten für den Datenverkehr.
```