Java Proxy & SSL
While security requirement increase in enterprises more and more Proxies will need special care. Let’s see up to date practices to manage them.
HTTP proxy in java
JVM Proxy
This will force the whole JVM to use proxy, including database access (unless excluded using nonProxyHosts)
System.setProperty("https.proxyHost", "172.27.0.254"); System.setProperty("https.proxyPort", "8080"); System.setProperty("https.nonProxyHosts", "localhost|127.*|[::1]");
-Dhttps.proxyHost=172.27.0.254 -Dhttps.proxyPort=8080 -Dhttp.nonProxyHosts="localhost|127.*|[::1]"
System.setProperty("http.proxyHost", "172.27.0.254"); System.setProperty("http.proxyPort", "8080"); System.setProperty("http.nonProxyHosts", "localhost|127.*|[::1]");
Can also be used from command line : -Dhttp.proxyHost=172.27.0.254 -Dhttp.proxyPort=8080
Proxy with authentication
When you receive a ‘407: Proxy Authentication Required’, you may need Proxy authentication :
Authenticator.setDefault(new Authenticator() < @Override protected PasswordAuthentication getPasswordAuthentication() < if (getRequestorType() == RequestorType.PROXY) < String prot = getRequestingProtocol().toLowerCase(); String host = System.getProperty(prot + ".proxyHost", ""); String port = System.getProperty(prot + ".proxyPort", "0"); String user = System.getProperty(prot + ".proxyUser", ""); String password = System.getProperty(prot + ".proxyPassword", ""); if (getRequestingHost().equalsIgnoreCase(host)) < if (Integer.parseInt(port) == getRequestingPort()) < return new PasswordAuthentication(user, password.toCharArray()); >> > return null; > >);
Once again this will impact the whole JVM and should be called once only.
System properties (proxyUser, proxyPassword) does not work :
@Test public void test_HttpURLConnection_setProperty_auth() throws IOException < // Required for JDK8.111+ System.setProperty("jdk.http.auth.tunneling.disabledSchemes", ""); System.setProperty("jdk.http.auth.proxying.disabledSchemes", ""); // URL url = new URL("https://www.example.com"); // System.setProperty("https.proxyHost", "172.27.0.254"); System.setProperty("https.proxyPort", "8080"); System.setProperty("https.proxyUser", "user"); System.setProperty("https.proxyPassword", "pass"); // Could be used System.setProperty("http.proxyHost", "172.27.0.254"); System.setProperty("http.proxyPort", "8080"); System.setProperty("http.proxyUser", "user"); System.setProperty("http.proxyPassword", "pass"); // HttpURLConnection con = (HttpURLConnection) url.openConnection(); /** * As an evidence, http*.proxyUser does not work */ assertEquals(407, con.getResponseCode()); assertEquals("Proxy Authentication Required", con.getResponseMessage()); >
URLConnection
java.net.Proxy
When using URLConnection a better way is possible using java.net.Proxy (which does not force the whole JVM) :
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("172.27.0.254", 8080); URL url = new URL("https://www.example.com); HttpURLConnection con = (HttpURLConnection) url.openConnection(proxy);
This can be used with a default Authenticator.
SOAPConnection
You can create to own SOAP request without any framework using SOAPConnection
SOAPConnection soapConnection = SOAPConnectionFactory.newInstance().createConnection(); SOAPMessage soapResponse = soapConnection.call(createSOAPRequest(), "https://salesforce.com/services/Soap/u/41.0");
But the SOAPConnectionFactory returns a com.sun.xml.internal.messaging.saaj.client.p2p.HttpSOAPConnection which does not use java.net.Proxy :
private HttpURLConnection createConnection(URL endpoint) throws IOException
So you have to use System properties to specify a Proxy server (hence impacting all JVM or playing with exclusions).
Apache
CXF
CXF uses URLConnection for synchronous calls, and HttpClient for asynchrounous ones. Later may also be forced for synchronous calls.
JaxWsProxyFactoryBean jaxWsProxyFactoryBean = new JaxWsProxyFactoryBean(); jaxWsProxyFactoryBean.setAddress("https://endpoint.soap"); jaxWsProxyFactoryBean.setServiceClass(MyInterface.class); MyInterface myService = (MyInterface) jaxWsProxyFactoryBean.create(); Client client = ClientProxy.getClient(myService); HTTPConduit httpConduit = (HTTPConduit) client.getConduit(); HTTPClientPolicy httpClientPolicy = httpConduit.getClient(); httpClientPolicy.setProxyServer("172.27.0.254"); httpClientPolicy.setProxyServerPort(8080); httpClientPolicy.setProxyServerType(ProxyServerType.HTTP); // If proxy Authentication : httpConduit.getProxyAuthorization().setUserName("user"); httpConduit.getProxyAuthorization().setPassword("passw");
HttpClient
HttpClient also uses HTTPConduit to configure your Proxy settings.
Spring Rest Template
private final RestTemplate getProxiedRestTemplate() < RestTemplate r = null; if (isProxyActivated()) < HttpClientBuilder client = HttpClientBuilder.create(); HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(); client.setProxy(new HttpHost("172.27.0.254", 8080)); if (withAuthentication()) < Credentials credentials = new UsernamePasswordCredentials(getProxyUsername(), getProxyPassword()); AuthScope authScope = new AuthScope("172.27.0.254", 8080); CredentialsProvider credsProvider = new BasicCredentialsProvider(); credsProvider.setCredentials(authScope, credentials); client.setDefaultCredentialsProvider(credsProvider); >requestFactory.setHttpClient(client.build()); r = new RestTemplate(requestFactory); > else < r = new RestTemplate(); >return r; >
JDK 8.111+
Disable Basic authentication for HTTPS tunneling In some environments, certain authentication schemes may be undesirable when proxying HTTPS. Accordingly, the Basic authentication scheme has been deactivated, by default, in the Oracle Java Runtime, by adding Basic to the jdk.http.auth.tunneling.disabledSchemes networking property. Now, proxies requiring Basic authentication when setting up a tunnel for HTTPS will no longer succeed by default. If required, this authentication scheme can be reactivated by removing Basic from the jdk.http.auth.tunneling.disabledSchemes networking property, or by setting a system property of the same name to “” ( empty ) on the command line.
Additionally, the jdk.http.auth.tunneling.disabledSchemes and jdk.http.auth.proxying.disabledSchemes networking properties, and system properties of the same name, can be used to disable other authentication schemes that may be active when setting up a tunnel for HTTPS, or proxying plain HTTP, respectively. JDK-8160838 (not public)
System.setProperty("jdk.http.auth.tunneling.disabledSchemes", ""); System.setProperty("jdk.http.auth.proxying.disabledSchemes", "");
Or as a JVM parameter in your command line :
-Djdk.http.auth.tunneling.disabledSchemes="" -Djdk.http.auth.proxying.disabledSchemes=""
Example to start your jetty server :
JAVA_OPTIONS="$JAVA_OPTIONS -Djdk.http.auth.tunneling.disabledSchemes="" -Djdk.http.auth.proxying.disabledSchemes="" "
SSL troubleshooting
System.setProperty("javax.net.debug", "all");// "ssl:record:plaintext" sun.util.logging.PlatformLogger.getLogger("sun.net.www.protocol.http.HttpURLConnection").setLevel(sun.util.logging.PlatformLogger.Level.ALL); sun.util.logging.PlatformLogger.getLogger("sun.net.www.protocol.http.HttpsURLConnection").setLevel(sun.util.logging.PlatformLogger.Level.ALL);
Enable old insecure protocols :
System.setProperty("https.protocols", "TLSv1,TLSv1.1,TLSv1.2,SSLv3");
Class HttpClient
An HttpClient can be used to send requests and retrieve their responses. An HttpClient is created through a builder . The newBuilder method returns a builder that creates instances of the default HttpClient implementation. The builder can be used to configure per-client state, like: the preferred protocol version ( HTTP/1.1 or HTTP/2 ), whether to follow redirects, a proxy, an authenticator, etc. Once built, an HttpClient is immutable, and can be used to send multiple requests.
An HttpClient provides configuration information, and resource sharing, for all requests sent through it.
A BodyHandler must be supplied for each HttpRequest sent. The BodyHandler determines how to handle the response body, if any. Once an HttpResponse is received, the headers, response code, and body (typically) are available. Whether the response body bytes have been read or not depends on the type, T , of the response body.
- send(HttpRequest, BodyHandler) blocks until the request has been sent and the response has been received.
- sendAsync(HttpRequest, BodyHandler) sends the request and receives the response asynchronously. The sendAsync method returns immediately with a CompletableFuture. The CompletableFuture completes when the response becomes available. The returned CompletableFuture can be combined in different ways to declare dependencies among several asynchronous tasks.
HttpClient client = HttpClient.newBuilder() .version(Version.HTTP_1_1) .followRedirects(Redirect.NORMAL) .connectTimeout(Duration.ofSeconds(20)) .proxy(ProxySelector.of(new InetSocketAddress("proxy.example.com", 80))) .authenticator(Authenticator.getDefault()) .build(); HttpResponse response = client.send(request, BodyHandlers.ofString()); System.out.println(response.statusCode()); System.out.println(response.body());
HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://foo.com/")) .timeout(Duration.ofMinutes(2)) .header("Content-Type", "application/json") .POST(BodyPublishers.ofFile(Paths.get("file.json"))) .build(); client.sendAsync(request, BodyHandlers.ofString()) .thenApply(HttpResponse::body) .thenAccept(System.out::println);
If a security manager is present then security checks are performed by the HTTP Client’s sending methods. An appropriate URLPermission is required to access the destination server, and proxy server if one has been configured. The form of the URLPermission required to access a proxy has a method parameter of «CONNECT» (for all kinds of proxying) and a URL string of the form «socket://host:port» where host and port specify the proxy’s address.
Class HttpClient
An HttpClient can be used to send requests and retrieve their responses. An HttpClient is created through a builder . The newBuilder method returns a builder that creates instances of the default HttpClient implementation. The builder can be used to configure per-client state, like: the preferred protocol version ( HTTP/1.1 or HTTP/2 ), whether to follow redirects, a proxy, an authenticator, etc. Once built, an HttpClient is immutable, and can be used to send multiple requests.
An HttpClient provides configuration information, and resource sharing, for all requests sent through it.
A BodyHandler must be supplied for each HttpRequest sent. The BodyHandler determines how to handle the response body, if any. Once an HttpResponse is received, the headers, response code, and body (typically) are available. Whether the response body bytes have been read or not depends on the type, T , of the response body.
- send(HttpRequest, BodyHandler) blocks until the request has been sent and the response has been received.
- sendAsync(HttpRequest, BodyHandler) sends the request and receives the response asynchronously. The sendAsync method returns immediately with a CompletableFuture. The CompletableFuture completes when the response becomes available. The returned CompletableFuture can be combined in different ways to declare dependencies among several asynchronous tasks.
HttpClient client = HttpClient.newBuilder() .version(Version.HTTP_1_1) .followRedirects(Redirect.NORMAL) .connectTimeout(Duration.ofSeconds(20)) .proxy(ProxySelector.of(new InetSocketAddress("proxy.example.com", 80))) .authenticator(Authenticator.getDefault()) .build(); HttpResponse response = client.send(request, BodyHandlers.ofString()); System.out.println(response.statusCode()); System.out.println(response.body());
HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://foo.com/")) .timeout(Duration.ofMinutes(2)) .header("Content-Type", "application/json") .POST(BodyPublishers.ofFile(Paths.get("file.json"))) .build(); client.sendAsync(request, BodyHandlers.ofString()) .thenApply(HttpResponse::body) .thenAccept(System.out::println);
If a security manager is present then security checks are performed by the HTTP Client’s sending methods. An appropriate URLPermission is required to access the destination server, and proxy server if one has been configured. The form of the URLPermission required to access a proxy has a method parameter of «CONNECT» (for all kinds of proxying) and a URL string of the form «socket://host:port» where host and port specify the proxy’s address.
How do I set a proxy for HttpClient?
In this example you will see how to configure proxy when using the Apache Commons HttpClient library.
package org.kodejava.commons.httpclient; import org.apache.commons.httpclient.Credentials; import org.apache.commons.httpclient.HostConfiguration; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpMethod; import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.httpclient.UsernamePasswordCredentials; import org.apache.commons.httpclient.auth.AuthScope; import org.apache.commons.httpclient.methods.GetMethod; import java.io.IOException; public class HttpGetProxy < private static final String PROXY_HOST = "proxy.host.com"; private static final int PROXY_PORT = 8080; public static void main(String[] args) < HttpClient client = new HttpClient(); HttpMethod method = new GetMethod("https://kodejava.org"); HostConfiguration config = client.getHostConfiguration(); config.setProxy(PROXY_HOST, PROXY_PORT); String username = "guest"; String password = "s3cr3t"; Credentials credentials = new UsernamePasswordCredentials(username, password); AuthScope authScope = new AuthScope(PROXY_HOST, PROXY_PORT); client.getState().setProxyCredentials(authScope, credentials); try < client.executeMethod(method); if (method.getStatusCode() == HttpStatus.SC_OK) < String response = method.getResponseBodyAsString(); System.out.println("Response language-xml"> commons-httpclient commons-httpclient 3.1
A programmer, recreational runner and diver, live in the island of Bali, Indonesia. Programming in Java, Spring, Hibernate / JPA. You can support me working on this project, buy me a cup of coffee ☕ every little bit helps, thank you 🙏