客户端证书和Docker的FeignClient

我有一个要求我的微服务利用双向ssl。 每个微服务都是Spring Boot Application,注释如下:

@SpringBootApplication @EnableFeignClients @EnableDiscoveryClient @EnableZuulProxy public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } 

每个yml都有类似于eureka / ribbon的configuration:

 eureka: client: service-url: defaultZone: ${EUREKA_CLIENT_SERVICEURL_PROTOCOL:http}://${EUREKA_CLIENT_SERVICEURL_HOST:192.168.99.100}:${EUREKA_CLIENT_SERVICEURL_PORT:8761}/eureka/ instance: secure-virtual-host-name: ${spring.application.name} prefer-ip-address: true non-secure-port-enabled: ${EUREKA_NON_SECURE_PORT_ENABLED:false} secure-port-enabled: ${EUREKA_SECURE_PORT_ENABLED:true} secure-port: ${server.port} ribbon: IsSecure: true eureka: enabled: true 

每个微服务都有一个控制器,为各种function公开api。

当一个微服务需要调用另一个微服务端点时,我试图通过创build该微服务的客户端接口来实现:

 @FeignClient(name = "user", configuration = FeignConfiguration.class, url = "https://user") public interface UserClient { @RequestMapping(method = RequestMethod.GET, value = "/test") String testUser(); } 

这是FeignConfiguration:

 @Configuration public class FeignConfiguration { @Bean public Feign.Builder feignBuilder(){ Client trustSSLSockets = new Client.Default(TrustingSSLSocketFactory.get(), null); return Feign.builder().client(trustSSLSockets); } @Bean Logger.Level feignLoggerLevel() { return Logger.Level.FULL; } @Bean public Contract feignContract() { return new feign.Contract.Default(); } } 

TrustingSSLSocketFactory类是从https://github.com/Netflix/feign/blob/master/core/src/test/java/feign/client/TrustingSSLSocketFactory.java复制的,并使用我的客户端证书/密码进行修改。

用户微服务和其他configuration中的安全configuration为:

 server: port: ${DOCKER_SERVER_PORT:28443} ssl: key-store: ${KEYSTORE_FILE:classpath:keystore.p12} key-store-password: ${KEYSTORE_PASSWORD:abc123} key-store-type: ${KEYSTORE_TYPE:PKCS12} trust-store: ${TRUSTSTORE_FILE:classpath:trust.jks} trust-store-password: ${TRUSTSTORE_PASSWORD:abc123} trust-store-type: ${TRUSTSTORE_TYPE:JKS} client-auth: ${CLIENT_AUTH_REQUIRED:want} 

为了调用客户端,我只需要注入客户端,并将其作为任何其他接口调用。

 @RestController public class MyController { @Autowired UserClient userClient; @RequestMapping(value = "/testUser", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) public String testUser() { return userClient.testUser(); } } 

用户微服务有一个testing端点,返回一个简单的答复。

当UserClient尝试与用户主机build立连接时,我得到一个UnknownHostException。 我可能没有正确实施,并会感谢一些指导。

 gateway-ms_1 | 2016-05-09 16:22:19.294 DEBUG 1 --- [qtp351251459-24] cnzuul.http.HttpServletRequestWrapper : Path = null gateway-ms_1 | 2016-05-09 16:22:19.294 DEBUG 1 --- [qtp351251459-24] cnzuul.http.HttpServletRequestWrapper : Transfer-Encoding = null gateway-ms_1 | 2016-05-09 16:22:19.294 DEBUG 1 --- [qtp351251459-24] cnzuul.http.HttpServletRequestWrapper : Content-Encoding = null gateway-ms_1 | 2016-05-09 16:22:19.295 DEBUG 1 --- [qtp351251459-24] cnzuul.http.HttpServletRequestWrapper : Content-Length header = -1 gateway-ms_1 | 2016-05-09 16:22:19.297 DEBUG 1 --- [qtp351251459-24] cnloadbalancer.ZoneAwareLoadBalancer : Zone aware logic disabled or there is only one zone gateway-ms_1 | 2016-05-09 16:22:19.298 DEBUG 1 --- [qtp351251459-24] cnloadbalancer.LoadBalancerContext : register using LB returned Server: 172.17.0.4:28443 for request /testUser gateway-ms_1 | 2016-05-09 16:22:19.298 DEBUG 1 --- [qtp351251459-24] com.netflix.niws.client.http.RestClient : RestClient sending new Request(GET: ) https://172.17.0.4:28443/testUser gateway-ms_1 | 2016-05-09 16:22:19.300 DEBUG 1 --- [qtp351251459-24] cnhttp4.MonitoredConnectionManager : Get connection: {s}->https://172.17.0.4:28443, timeout = 3000 gateway-ms_1 | 2016-05-09 16:22:19.300 DEBUG 1 --- [qtp351251459-24] com.netflix.http4.NamedConnectionPool : [{s}->https://172.17.0.4:28443] total kept alive: 1, total issued: 0, total allocated: 1 out of 200 gateway-ms_1 | 2016-05-09 16:22:19.300 DEBUG 1 --- [qtp351251459-24] com.netflix.http4.NamedConnectionPool : Getting free connection [{s}->https://172.17.0.4:28443][null] gateway-ms_1 | 2016-05-09 16:22:19.300 DEBUG 1 --- [qtp351251459-24] com.netflix.http4.NFHttpClient : Stale connection check gateway-ms_1 | 2016-05-09 16:22:19.305 DEBUG 1 --- [qtp351251459-24] com.netflix.http4.NFHttpClient : Attempt 1 to execute request register-ms_1 | 2016-05-09 16:22:19.312 DEBUG 1 --- [qtp376795121-24] com.jdh.register.clients.UserClient : [UserClient#testUser] ---> GET https://user/test HTTP/1.1 register-ms_1 | 2016-05-09 16:22:19.313 DEBUG 1 --- [qtp376795121-24] com.jdh.register.clients.UserClient : [UserClient#testUser] ---> END HTTP (0-byte body) register-ms_1 | 2016-05-09 16:22:19.422 DEBUG 1 --- [qtp376795121-24] com.jdh.register.clients.UserClient : [UserClient#testUser] <--- ERROR UnknownHostException: user (109ms) register-ms_1 | 2016-05-09 16:22:19.426 DEBUG 1 --- [qtp376795121-24] com.jdh.register.clients.UserClient : [UserClient#testUser] java.net.UnknownHostException: user register-ms_1 | at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:184) register-ms_1 | at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392) register-ms_1 | at java.net.Socket.connect(Socket.java:589) register-ms_1 | at sun.net.NetworkClient.doConnect(NetworkClient.java:175) register-ms_1 | at sun.net.www.http.HttpClient.openServer(HttpClient.java:432) register-ms_1 | at sun.net.www.http.HttpClient.openServer(HttpClient.java:527) register-ms_1 | at sun.net.www.protocol.https.HttpsClient.<init>(HttpsClient.java:275) register-ms_1 | at sun.net.www.protocol.https.HttpsClient.New(HttpsClient.java:371) register-ms_1 | at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.getNewHttpClient(AbstractDelegateHttpsURLConnection.java:191) register-ms_1 | at sun.net.www.protocol.http.HttpURLConnection.plainConnect0(HttpURLConnection.java:1104) register-ms_1 | at sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:998) register-ms_1 | at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:177) register-ms_1 | at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1512) evice-ms_1 | at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1440) 

Client是色带注入的地方。 通过创build自己的客户端,您已select退出function区,因此http://user/...不能通过function区parsing。

这是我们如何创build负载平衡器客户端

 @Bean @ConditionalOnMissingBean public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory, SpringClientFactory clientFactory) { return new LoadBalancerFeignClient(new Client.Default(null, null), cachingFactory, clientFactory); } 

另外,通过使用feign.Contract.Default()您可以select不使用Spring MVC注释,比如@RequestMapping ,默认使用feign.Contract.Default()的注释。