I'm trying to create a FeignClient for one external HTTP API which uses SSL. The struggle is - how to modify default Spring FeignClient with my logic, in this case SSL Connection Factory. So basically I wanna keep all the good things Spring automatically does for the FeignClients, like Hystrix, Sleuth tracing, etc and make it work with my SSL factory.
Will appreciate any suggestions.
Here is what I tried to do:
I've tried to provide a custom @Configuration outside of ComponentScan:
@Configuration
public class CustomFeignConfiguration
{
@Bean
public Feign.Builder feignBuilder()
{
Client trustSSLSockets = new Client.Default(
TrustingSSLSocketFactory.get("server1"),
new NoopHostnameVerifier());
log.info("feignBuilder called");
return Feign.builder().client(trustSSLSockets);
}
...
}
Made FeignClient use it through annotation
@FeignClient(name = "sslClient", configuration = CustomFeignConfiguration.class, url = "https://...")
Where "TrustingSSLSocketFactory" is implemented similar to this.
Now if I inject my client in a Spring application, I can see that "feignBuilder" was called and it successfully loaded my keys. The problem is that the created client doesn't actually use the specified SSLFactory for createSocket calls. So I get:
Caused by: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
at sun.security.ssl.Alerts.getSSLException(Alerts.java:192) ~[na:1.8.0_72]
at sun.security.ssl.Alerts.getSSLException(Alerts.java:154) ~[na:1.8.0_72]
at sun.security.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.java:2023) ~[na:1.8.0_72]
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1125) ~[na:1.8.0_72]
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1375) ~[na:1.8.0_72]
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1403) ~[na:1.8.0_72]
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1387) ~[na:1.8.0_72]
at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:559) ~[na:1.8.0_72]
at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185) ~[na:1.8.0_72]
at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1513) ~[na:1.8.0_72]
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1441) ~[na:1.8.0_72]
at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:480) ~[na:1.8.0_72]
at sun.net.www.protocol.https.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:338) ~[na:1.8.0_72]
at feign.Client$Default.convertResponse(Client.java:152) ~[feign-core-9.3.1.jar:na]
Create the Client
as it's own @Bean
rather than part of the builder. builder.client(client)
gets called later on, overriding the client you set while creating the builder.
So
@Bean
public Client feignClient()
{
Client trustSSLSockets = new Client.Default(
TrustingSSLSocketFactory.get("server1"),
new NoopHostnameVerifier());
log.info("feignClient called");
return trustSSLSockets;
}
I did something like this which served my purpose-
@Bean
public Client feignClient()
{
Client trustSSLSockets = new Client.Default(getSSLSocketFactory(), new NoopHostnameVerifier());
log.info("feignClient called");
return trustSSLSockets;
}
private SSLSocketFactory getSSLSocketFactory() {
try {
TrustStrategy acceptingTrustStrategy = new TrustStrategy() {
@Override
public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
//Do your validations
return true;
}
};
SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();
return sslContext.getSocketFactory();
} catch (Exception exception) {
throw new RuntimeException(exception);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With