2017-02-17 7 views
2

私はSpringクラウドユーレカサーバでhttpsを有効にしようとしています。 YAMLの設定:Spring Cloud Eurekaのデフォルト検出クライアントのデフォルトSSLコンテキストを無効にする方法は?

server: 
    port: 8100 
ssl: 
    clientAuth: want 
    protocol: TLS 
    key-store: classpath:keystore/keystore.jks 
    key-store-password: some 
    key-password: some 
eureka: 
    instance: 
    prefer-ip-address: true 
    non-secure-port-enabled: false 
    secure-port-enabled: true 
    secure-port: ${server.port} 
    healthCheckUrl: https://${eureka.hostname}:${secure-port}/health 
    statusPageUrl: https://${eureka.hostname}:${secure-port}/info 
    homePageUrl: https://${eureka.hostname}:${secure-port}/ 
security: 
    basic: 
    enabled: true 

その後、私は、サーバーに登録するためのクライアントを起動します。私は自己署名証明書をインポートしていないので、私はexcptionを取得するsun.security.provider.certpath.SunCertPathBuilderException:要求されたターゲットへの有効な証明書パスを見つけることができません。たくさんのインスタンスと証明書管理コストがあるので、私は証明書をインポートしたくありません。したがって私はclasspathにcertを入れ、起動時に読み込みます。私はそれが装うを使用して他のユーレカクライアントを呼び出したときにそれが正常に動作コード

SSLContext.setDefault(sslContext); 
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory()); 

を追加することで、クライアントのデフォルトのSSLコンテキストおよびSSLソケットfacotoryを上書きします。しかし、それはユーレカサーバに登録するときに労力を必要としません。 私はソースコードをチェックして、jersey、jerseyを使ってユーレカディスカバリクライアントがhttp apacheクライアントを呼び出すのを見つけます。 トラブルは、それがSchemeRegistryFactory.createDefault()ます最終コールSSLContexts.createDefault()考慮システムプロパティを取られないであろうを使用し、です。つまり、このhttpクライアントはカスタムSSLContextをトークン化しません。 私の質問は、ユーレカ発見クライアントでデフォルトのhttpクライアントを追加/再登録/置き換える方法ですか?

答えて

5

最後に、ソースコードを何度も掘り下げた後で解決策を見つけました。私はeureka-client-1.4.12というCamden.SR5バージョンを使用しています。

あなたはDiscoveryClientOptionalArgsEurekaJerseyClientを提供した場合は、ディスカバリークライアントは、デフォルトの1をintialません。クラスDiscoveryClientのコードの一部です。

private void scheduleServerEndpointTask(EurekaTransport eurekaTransport, 
             DiscoveryClientOptionalArgs args) { 
... 

    EurekaJerseyClient providedJerseyClient = args == null 
      ? null 
      : args.eurekaJerseyClient; 

    eurekaTransport.transportClientFactory = providedJerseyClient == null 
      ? TransportClientFactories.newTransportClientFactory(clientConfig, additionalFilters, applicationInfoManager.getInfo()) 
      : TransportClientFactories.newTransportClientFactory(additionalFilters, providedJerseyClient); 
... 
} 

それから私は、DiscoveryClientOptionalArgs Beanを作成するクラスを追加します。

import com.netflix.discovery.DiscoveryClient; 
import com.netflix.discovery.EurekaClientConfig; 
import com.netflix.discovery.converters.wrappers.CodecWrappers; 
import com.netflix.discovery.shared.transport.jersey.EurekaJerseyClient; 
import com.qy.insurance.cloud.core.eureka.CustomEurekaJerseyClientBuilder; 
import lombok.extern.slf4j.Slf4j; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.beans.factory.annotation.Value; 
import org.springframework.context.annotation.Bean; 
import org.springframework.context.annotation.Configuration; 

@Configuration 
@Slf4j 
public class EurekaSslConfig { 

@Value("${eureka.client.service-url.defaultZone}") 
private String defaultZone; 

@Autowired 
private EurekaClientConfig config; 

@Autowired 
private DefaultSslConfig defaultSslConfig; 

@Bean 
public DiscoveryClient.DiscoveryClientOptionalArgs discoveryClientOptionalArgs(){ 
    if(!defaultSslConfig.isFinish()){ 
     log.warn("Default SSLContext might not have been updated! Please check!"); 
    } 

    DiscoveryClient.DiscoveryClientOptionalArgs args = new DiscoveryClient.DiscoveryClientOptionalArgs(); 

    CustomEurekaJerseyClientBuilder clientBuilder = new CustomEurekaJerseyClientBuilder() 
      .withClientName("DiscoveryClient-HTTPClient-Custom") 
      .withUserAgent("Java-EurekaClient") 
      .withConnectionTimeout(config.getEurekaServerConnectTimeoutSeconds() * 1000) 
      .withReadTimeout(config.getEurekaServerReadTimeoutSeconds() * 1000) 
      .withMaxConnectionsPerHost(config.getEurekaServerTotalConnectionsPerHost()) 
      .withMaxTotalConnections(config.getEurekaServerTotalConnections()) 
      .withConnectionIdleTimeout(config.getEurekaConnectionIdleTimeoutSeconds() * 1000) 
      .withEncoderWrapper(CodecWrappers.getEncoder(config.getEncoderName())) 
      .withDecoderWrapper(CodecWrappers.resolveDecoder(config.getDecoderName(), config.getClientDataAccept())); 
    if (defaultZone.startsWith("https://")) { 
     clientBuilder.withSystemSSLConfiguration(); 
    } 

    EurekaJerseyClient jerseyClient = clientBuilder.build(); 
    args.setEurekaJerseyClient(jerseyClient);//Provide custom EurekaJerseyClient to override default one 
    return args; 
} 

}

うまく機能して私のカスタムEurekaJerseyClientを保証するために、私はEurekaJerseyClientImplからコードをフォークし、いくつかの変更を行いました。

import com.netflix.discovery.converters.wrappers.CodecWrappers; 
import com.netflix.discovery.converters.wrappers.DecoderWrapper; 
import com.netflix.discovery.converters.wrappers.EncoderWrapper; 
import com.netflix.discovery.provider.DiscoveryJerseyProvider; 
import com.netflix.discovery.shared.MonitoredConnectionManager; 
import com.netflix.discovery.shared.transport.jersey.EurekaJerseyClient; 
import com.netflix.discovery.shared.transport.jersey.EurekaJerseyClientImpl; 
import com.netflix.discovery.shared.transport.jersey.SSLSocketFactoryAdapter; 
import com.netflix.discovery.util.DiscoveryBuildInfo; 
import com.sun.jersey.api.client.config.ClientConfig; 
import com.sun.jersey.client.apache4.config.ApacheHttpClient4Config; 
import com.sun.jersey.client.apache4.config.DefaultApacheHttpClient4Config; 
import org.apache.http.client.params.ClientPNames; 
import org.apache.http.conn.scheme.PlainSocketFactory; 
import org.apache.http.conn.scheme.Scheme; 
import org.apache.http.conn.scheme.SchemeRegistry; 
import org.apache.http.conn.ssl.SSLConnectionSocketFactory; 
import org.apache.http.conn.ssl.SSLSocketFactory; 
import org.apache.http.conn.ssl.X509HostnameVerifier; 
import org.apache.http.impl.conn.SchemeRegistryFactory; 
import org.apache.http.params.CoreProtocolPNames; 
import org.apache.http.util.TextUtils; 

public class CustomEurekaJerseyClientBuilder { 
private boolean systemSSL; 
private String clientName; 
private int maxConnectionsPerHost; 
private int maxTotalConnections; 
private String trustStoreFileName; 
private String trustStorePassword; 
private String userAgent; 
private String proxyUserName; 
private String proxyPassword; 
private String proxyHost; 
private String proxyPort; 
private int connectionTimeout; 
private int readTimeout; 
private int connectionIdleTimeout; 
private EncoderWrapper encoderWrapper; 
private DecoderWrapper decoderWrapper; 

public CustomEurekaJerseyClientBuilder withClientName(String clientName) { 
    this.clientName = clientName; 
    return this; 
} 

public CustomEurekaJerseyClientBuilder withUserAgent(String userAgent) { 
    this.userAgent = userAgent; 
    return this; 
} 

public CustomEurekaJerseyClientBuilder withConnectionTimeout(int connectionTimeout) { 
    this.connectionTimeout = connectionTimeout; 
    return this; 
} 

public CustomEurekaJerseyClientBuilder withReadTimeout(int readTimeout) { 
    this.readTimeout = readTimeout; 
    return this; 
} 

public CustomEurekaJerseyClientBuilder withConnectionIdleTimeout(int connectionIdleTimeout) { 
    this.connectionIdleTimeout = connectionIdleTimeout; 
    return this; 
} 

public CustomEurekaJerseyClientBuilder withMaxConnectionsPerHost(int maxConnectionsPerHost) { 
    this.maxConnectionsPerHost = maxConnectionsPerHost; 
    return this; 
} 

public CustomEurekaJerseyClientBuilder withMaxTotalConnections(int maxTotalConnections) { 
    this.maxTotalConnections = maxTotalConnections; 
    return this; 
} 

public CustomEurekaJerseyClientBuilder withProxy(String proxyHost, String proxyPort, String user, String password) { 
    this.proxyHost = proxyHost; 
    this.proxyPort = proxyPort; 
    this.proxyUserName = user; 
    this.proxyPassword = password; 
    return this; 
} 

public CustomEurekaJerseyClientBuilder withSystemSSLConfiguration() { 
    this.systemSSL = true; 
    return this; 
} 

public CustomEurekaJerseyClientBuilder withTrustStoreFile(String trustStoreFileName, String trustStorePassword) { 
    this.trustStoreFileName = trustStoreFileName; 
    this.trustStorePassword = trustStorePassword; 
    return this; 
} 

public CustomEurekaJerseyClientBuilder withEncoder(String encoderName) { 
    return this.withEncoderWrapper(CodecWrappers.getEncoder(encoderName)); 
} 

public CustomEurekaJerseyClientBuilder withEncoderWrapper(EncoderWrapper encoderWrapper) { 
    this.encoderWrapper = encoderWrapper; 
    return this; 
} 

public CustomEurekaJerseyClientBuilder withDecoder(String decoderName, String clientDataAccept) { 
    return this.withDecoderWrapper(CodecWrappers.resolveDecoder(decoderName, clientDataAccept)); 
} 

public CustomEurekaJerseyClientBuilder withDecoderWrapper(DecoderWrapper decoderWrapper) { 
    this.decoderWrapper = decoderWrapper; 
    return this; 
} 

public EurekaJerseyClient build() { 
    MyDefaultApacheHttpClient4Config config = new MyDefaultApacheHttpClient4Config(); 
    try { 
     return new EurekaJerseyClientImpl(connectionTimeout, readTimeout, connectionIdleTimeout, config); 
    } catch (Throwable e) { 
     throw new RuntimeException("Cannot create Jersey client ", e); 
    } 
} 

class MyDefaultApacheHttpClient4Config extends DefaultApacheHttpClient4Config { 

    private static final String PROTOCOL = "https"; 
    private static final String PROTOCOL_SCHEME = "SSL"; 
    private static final int HTTPS_PORT = 443; 
    private static final String KEYSTORE_TYPE = "JKS"; 

    MyDefaultApacheHttpClient4Config() { 
     MonitoredConnectionManager cm; 

     if (systemSSL) { 
      cm = createSystemSslCM(); 
     } else { 
      cm = createDefaultSslCM(); 
     } 

     if (proxyHost != null) { 
      addProxyConfiguration(cm); 
     } 

     DiscoveryJerseyProvider discoveryJerseyProvider = new DiscoveryJerseyProvider(encoderWrapper, decoderWrapper); 
     getSingletons().add(discoveryJerseyProvider); 

     // Common properties to all clients 
     cm.setDefaultMaxPerRoute(maxConnectionsPerHost); 
     cm.setMaxTotal(maxTotalConnections); 
     getProperties().put(ApacheHttpClient4Config.PROPERTY_CONNECTION_MANAGER, cm); 

     String fullUserAgentName = (userAgent == null ? clientName : userAgent) + "/v" + DiscoveryBuildInfo.buildVersion(); 
     getProperties().put(CoreProtocolPNames.USER_AGENT, fullUserAgentName); 

     // To pin a client to specific server in case redirect happens, we handle redirects directly 
     // (see DiscoveryClient.makeRemoteCall methods). 
     getProperties().put(ClientConfig.PROPERTY_FOLLOW_REDIRECTS, Boolean.FALSE); 
     getProperties().put(ClientPNames.HANDLE_REDIRECTS, Boolean.FALSE); 
    } 

    private void addProxyConfiguration(MonitoredConnectionManager cm) { 
     if (proxyUserName != null && proxyPassword != null) { 
      getProperties().put(ApacheHttpClient4Config.PROPERTY_PROXY_USERNAME, proxyUserName); 
      getProperties().put(ApacheHttpClient4Config.PROPERTY_PROXY_PASSWORD, proxyPassword); 
     } else { 
      // Due to bug in apache client, user name/password must always be set. 
      // Otherwise proxy configuration is ignored. 
      getProperties().put(ApacheHttpClient4Config.PROPERTY_PROXY_USERNAME, "guest"); 
      getProperties().put(ApacheHttpClient4Config.PROPERTY_PROXY_PASSWORD, "guest"); 
     } 
     getProperties().put(DefaultApacheHttpClient4Config.PROPERTY_PROXY_URI, "http://" + proxyHost + ":" + proxyPort); 
    } 

    private MonitoredConnectionManager createSystemSslCM() { 
     MonitoredConnectionManager cm; 
     X509HostnameVerifier hostnameVerifier = SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER; 
     SSLConnectionSocketFactory systemSocketFactory = new SSLConnectionSocketFactory(
       (javax.net.ssl.SSLSocketFactory) javax.net.ssl.SSLSocketFactory.getDefault(), 
       split(System.getProperty("https.protocols")), 
       split(System.getProperty("https.cipherSuites")), 
       hostnameVerifier); 
     SSLSocketFactory sslSocketFactory = new SSLSocketFactoryAdapter(systemSocketFactory); 
     SchemeRegistry sslSchemeRegistry = new SchemeRegistry(); 
     sslSchemeRegistry.register(new Scheme(PROTOCOL, HTTPS_PORT, sslSocketFactory)); 
     cm = new MonitoredConnectionManager(clientName, sslSchemeRegistry); 
     return cm; 
    } 

    /** 
    * @see SchemeRegistryFactory#createDefault() 
    */ 
    private MonitoredConnectionManager createDefaultSslCM() { 
     final SchemeRegistry registry = new SchemeRegistry(); 
     registry.register(
       new Scheme("http", 80, PlainSocketFactory.getSocketFactory())); 
     registry.register(
       new Scheme("https", 443, new SSLSocketFactoryAdapter(SSLConnectionSocketFactory.getSocketFactory()))); 
     return new MonitoredConnectionManager(clientName, registry); 
    } 

    private String[] split(final String s) { 
     if (TextUtils.isBlank(s)) { 
      return null; 
     } 
     return s.split(" *, *"); 
    } 
} 
} 

これは、私と同じようにProduction JVMで証明書をインポートするのが容易ではない人に役立ちます。

関連する問題