前两天需要通过springboot-feign来调用一个https的外部服务接口,因此要实现feign-client的SSL设置。
feign执行http请求通常会调用feign.Client接口的实现。这个接口的默认实现类Default提供了添加SSL配置的构造器:
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15  | 
						public static class Default implements Client {     private final SSLSocketFactory sslContextFactory;     private final HostnameVerifier hostnameVerifier;     /**      * Null parameters imply platform defaults.      */     public Default(SSLSocketFactory sslContextFactory, HostnameVerifier hostnameVerifier) {       this.sslContextFactory = sslContextFactory;       this.hostnameVerifier = hostnameVerifier;     }     ... }  | 
					
简单介绍下构造器参数中用到的SSL相关的接口:
SSLSocketFactory:SSLSocket工厂;SSLSocket扩展了Socket并提供使用SSL或TLS协议的安全套接字。这种套接字是正常的流套接字,但是它们在基础网络传输协议(如TCP)上添加了安全保护层。HostnameVerifier:实现主机名验证功能;在握手期间,如果URL的主机名和服务器的标识主机名不匹配,则验证机制可以回调此接口实现程序来确定是否应该允许此连接,如果回调内实现不恰当,默认接受所有域名,则有安全风险。
默认使用的Client实例的两个构造参数的值都是null。我们需要在配置类中创建使用SSL的Client实例,这里参考了默认的配置类FeignClientsConfiguration:
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23  | 
						@Configuration public class SSLFeignClientConfig {     private static Logger logger = LoggerFactory.getLogger(SSLFeignClientConfig.class);     @Bean     @Scope("prototype")     @ConditionalOnMissingBean     public Feign.Builder feignBuilder() {         Client client;         try {             SSLContext context =                     new SSLContextBuilder()                             .loadTrustMaterial(null, (chain, authType) -> true)                             .build();             client = new Client.Default(context.getSocketFactory(), new NoopHostnameVerifier());         } catch (Exception e) {             logger.error("Create feign client with SSL config failed", e);             client = new Client.Default(null, null);         }         return Feign.builder().client(client);     } }  | 
					
代码中使用了SSLContext的getSocketFactory方法来创建 SSLSocketFactory对象。在创建SSLContext对象时,通过(chain, authType) -> true指定了SSL信任策略,即:不做任何处理,信任全部请求。
另一个HostnameVerifier类型的参数使用了一个实现类NoopHostnameVerifier的实例,作用是关闭主机名验证功能,并且永远不会抛出SSLException。
最后将配置添加到FeignClient注解上即可:
| 
					 1 2 3  | 
						@FeignClient(name = "zhyeaApiService",         url = "${zhyea-url}",          configuration = {FeignClientsConfiguration.class, SSLFeignClientConfig.class})  | 
					
如代码中所示可以为configuration项一次指定多个配置类。这里的第一个配置类是spring默认提供的配置类。
另外,如需要创建Client的Bean实例,在使用Eureka的情况下,应选择LoadBalancerFeignClient的对象,不然会产生诸如无法解析host之类的问题。
就这样了。
发表评论