【技术分享】一、 SpringCloud Fegin融合okhttp 踩坑记录(含跳过SSL证书验证)
- 踩坑bug
- 1. 默认fegin通过https请求绕过SSL证书验证
- 2. 将默认fegin调用方式改为Okhttp
- 3.确认OkhttpClient类型
- 4.Okhttp配置跳过SSL证书踩坑!!!
- 5.okhttp与jdk版本不兼容问题
踩坑bug
- clientBuilder.sslSocketFactory(SSLSocketFactory) not supported on JDK 9+
- java.net.UnknownHostException: 找不到请求地址(consul)
1. 默认fegin通过https请求绕过SSL证书验证
使用默认fegin配置 跳过SSL证书校验
。
@Slf4j
@Configuration
public class FeignConfig {
@Bean
Logger.Level deviceSendCmdFeignLoggerLevel() {
return Logger.Level.FULL;
}
@Bean
@ConditionalOnMissingBean
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
SpringClientFactory clientFactory) throws NoSuchAlgorithmException, KeyManagementException {
SSLContext ctx = SSLContext.getInstance("SSL");
X509TrustManager tm = new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
ctx.init(null, new TrustManager[]{tm}, null);
return new LoadBalancerFeignClient(new Client.Default(ctx.getSocketFactory(),
new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
}),
cachingFactory, clientFactory);
}
}
pom文件。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<!-- 使用Apache HttpClient替换Feign原生httpclient -->
<dependency>
<!--不是netfix包下了,应该是独立出来开源了-->
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
<version>9.4.0</version>
<!-- <version>9.5.1</version> 这里可以不指定version spring-boot-parent中已经有版本定义-->
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2. 将默认fegin调用方式改为Okhttp
- applicattion.yml配置
feign:
httpclient: --- 关闭
enabled: false
okhttp: --- 开启
enabled: true
hystrix:
enabled: true
- pom文件
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
</dependency>
3.确认OkhttpClient类型
- 到此为止
- 如果不需要配置跳过SSL证书验证 底层就已经切换为OkhttpClient调用方式了
验证:SynchronousMethodHandler类中查看 请求request使用的fegin类型
public Object invoke(Object[] argv) throws Throwable {
RequestTemplate template = this.buildTemplateFromArgs.create(argv);
Options options = this.findOptions(argv);
Retryer retryer = this.retryer.clone();
while(true) {
try {
return this.executeAndDecode(template, options);
} catch (RetryableException var9) {...........}
}
}
Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
Request request = this.targetRequest(template);
if (this.logLevel != Level.NONE) {
this.logger.logRequest(this.metadata.configKey(), this.logLevel, request);
}
long start = System.nanoTime();
Response response;
try {
<!--看这 查看clent类型 -->
<!--看这 查看clent类型 -->
response = this.client.execute(request, options);
<!--看这 查看clent类型 -->
<!--看这 查看clent类型 -->
response = response.toBuilder().request(request).requestTemplate(template).build();
} catch (IOException var13) {
..................
}
}
4.Okhttp配置跳过SSL证书踩坑!!!
- 使用步骤一中的方式直接注入OkhttpClient示例的方式走不通,Client.Default 没有对代码实现注入方法 即使注入代码不报错 服务还是调用不成功的
- 采用对 okhttp 直接配置的方式 注入 configuration
@Configuration
@ConditionalOnClass(Feign.class)
@AutoConfigureBefore(FeignAutoConfiguration.class)
public class FeignOkHttpConfig {
@Bean
public okhttp3.OkHttpClient okHttpClient(){
okhttp3.OkHttpClient.Builder builder = new okhttp3.OkHttpClient.Builder();
try {
final TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) {
}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) {
}
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[]{};
}
}
};
final SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
builder.readTimeout(60, TimeUnit.SECONDS);
builder.connectTimeout(60, TimeUnit.SECONDS);
builder.sslSocketFactory(sslSocketFactory);
builder.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
} catch (Exception e) {
throw new RuntimeException(e);
}
builder.connectionPool(new ConnectionPool());
return builder.build();
}
}
- 到这你是不是以为问题解决了???? 不可能!!!还有个大坑 且听我娓娓道来
5.okhttp与jdk版本不兼容问题
错误【clientBuilder.sslSocketFactory(SSLSocketFactory) not supported on JDK 9+】:
- 针对jdk 1.8.0_251、okhttp4.3.0以下版本不兼容问题
详细过程分析 请看 链接: link. - 解决方法1:调用sslSocketFactory时候传入trustManager。(推荐此做法,无论okhttp、okhttp版本是什么,都不会报错)。
@Configuration
@ConditionalOnClass(Feign.class)
@AutoConfigureBefore(FeignAutoConfiguration.class)
public class FeignOkHttpConfig {
@Bean
public okhttp3.OkHttpClient okHttpClient(){
okhttp3.OkHttpClient.Builder builder = new okhttp3.OkHttpClient.Builder();
try {
final TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) {
}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) {
}
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[]{};
}
}
};
final SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
<!-- 区别在这 -->
<!-- 区别在这 -->
X509TrustManager trustAllCert = (X509TrustManager) trustAllCerts[0];
builder.sslSocketFactory(sslSocketFactory,trustAllCert);
<!-- 区别在这 -->
<!-- 区别在这 -->
builder.readTimeout(60, TimeUnit.SECONDS);
builder.connectTimeout(60, TimeUnit.SECONDS);
builder.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
} catch (Exception e) {
throw new RuntimeException(e);
}
builder.connectionPool(new ConnectionPool());
return builder.build();
}
}
- 升级okhttp4.3.0以上版本或降级至jdk1.8.0_251之前版本。