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
https://docs.oracle.com/javase/7/docs/api/java/net/doc-files/net-properties.html#Proxies
JVM Proxy
This will force the whole JVM to use proxy, including database access (unless excluded using nonProxyHosts)
For https URL
System.setProperty("https.proxyHost", "172.27.0.254");
System.setProperty("https.proxyPort", "8080");
System.setProperty("https.nonProxyHosts", "localhost|127.*|[::1]");
Or using command line :
-Dhttps.proxyHost=172.27.0.254 -Dhttps.proxyPort=8080 -Dhttp.nonProxyHosts="localhost|127.*|[::1]"
For http URL
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.
See https://rolandtapken.de/blog/2012-04/java-process-httpproxyuser-and-httpproxypassword
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 {
return (HttpURLConnection)endpoint.openConnection();
}
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");
Note : Spring can help you to configure your HTTPConduit : see http://cxf.apache.org/docs/client-http-transport-including-ssl-support.html#ClientHTTPTransport(includingSSLsupport)-Theconduitelement
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+
http://www.oracle.com/technetwork/java/javase/8u111-relnotes-3124969.html
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 :
vi /etc/default/jetty
JAVA_OPTIONS="$JAVA_OPTIONS -Djdk.http.auth.tunneling.disabledSchemes="" -Djdk.http.auth.proxying.disabledSchemes="" "
SSL troubleshooting
Enable logs :
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");