【问题标题】:SpringBoot+Eureka+CloudConfig in a SSL environmentSSL环境下的Spring Boot+Eureka+Cloud Config
【发布时间】:2018-12-01 00:19:57
【问题描述】:

我想为微服务环境进行以下加密设置。一个启用 SSL 的 Eureka 服务器,带有云配置服务和多个微服务,只需要连接到发现服务、读取配置并运行它们的东西。一切都通过 HTTPS 和身份验证。

所以...我做了什么:

Eureka Discovery Server,包括 /config/* 上的云配置服务 服务器正在将自己注册到 Eureka 服务器,并且 Eureka Dashboard 显示了该实例。此服务在启用 SSL 和用户身份验证的端口 9001 上运行。该服务照常工作。

然后我创建了一个新的 SpringBoot 服务,它连接到 Eureka 并将自己注册到它。由于是自签名证书,我从这篇文章中写了一个小的 SSLConfguration 类:How to override Spring Cloud Eureka default discovery client default ssl context? 将我的个人信任库提供给底层的 EurekaClient。 我们所有的服务都配置了 application.yml:

spring:
    application:
        name: mark2

server:
    port: 9998
    ssl:
        enabled: true
        key-store: classpath:keystore.p12
        key-store-password: changeit
        key-store-type: PKCS12
        keyAlias: mark2

http:
    client:
      ssl:
          trust-store: classpath:keystore.p12
          trust-store-password: changeit

eureka:
    client:
        serviceUrl:
            defaultZone: https://user:pass@localhost:9001/eureka

客户端完美连接注册。

现在我希望 SpringBoot 服务注册到 Eureka Server 并从 Config Server 加载配置 - 在引导期间。所以我把 spring、http 和 eureka 设置移到 bootstrap.yml

spring:
    application:
        name: mark2
    cloud:
        config:
            username: user
            password: password
            discovery:
                enabled: true
                serviceId: EUREKA-DISCOVERY-SERVICE
            failFast: true
            name: my
            profile: settings
#            label: v1

http:
    client:
      ssl:
          trust-store: classpath:keystore.p12
          trust-store-password: changeit

eureka:
    client:
        serviceUrl:
            defaultZone: https://user:password@localhost:9001/eureka
    instance:
        metadataMap:
            user: user
            password: password
            configPath: /config

如果服务启动,它会在引导过程中尝试连接到 Eureka 服务器。这不起作用,因为它不信任来自 Eureka 服务器的自签名证书。我不能使用@Configure 或@Bean,因为我在引导程序中(至少我发现没有任何工作)。使用 SSLConfiguration 的上述解决方案也不起作用。

好的,然后我将带有 javax.net.ssl.trustStore 等的 JVM 参数设置为我的个人信任库。

bootRun {
   jvmArgs = [ 
     "-Djavax.net.ssl.trustStore=/absolute/path/to/keystore.p12", 
     "-Djavax.net.ssl.trustStorePassword=changeit", 
     "-Djavax.net.ssl.trustStoreType=PKCS12", 
   ]
}

服务启动,Eureka 客户端开始与 Eureka 服务器通信,找到一个 Cloud Configuration 实例并加载一个属性文件并创建 Spring 上下文。该属性仅包含一些测试属性。

然后 - 错误:

Field optionalArgs in org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration$RefreshableEurekaClientConfiguration required a single bean, but 2 were found:
    - getTrustStoredEurekaClient: defined by method 'getTrustStoredEurekaClient' in class path resource [de/mark2/SslConfiguration.class]
    - discoveryClientOptionalArgs: defined by method 'discoveryClientOptionalArgs' in org.springframework.cloud.netflix.eureka.config.DiscoveryClientOptionalArgsConfiguration

好的,不好但还好。因为我们给了 JVM 参数,所以我们不应该再使用 SSLConfiguration。已删除。

现在 - 一切正常,服务应该启动嵌入式 Tomcat。但这不起作用,因为另一个异常说,配置的密钥库为空(“trustAnchors 参数必须为非空”表示密钥库存在但不包含任何别名证书)。我认为,嵌入式 Tomcat 中使用了 JVM 参数,它尝试将信任库用于某种 ClientCertificates 左右。我不知道。

如果我禁用云配置 - 嵌入式 Tomcat 仍然有相同的错误。如果我也删除 JVM 参数 - 服务就会出现。

所以 - 我不知道我需要做什么,在引导期间加密通信而不为 Eureka 客户端定义 JVM 参数或给嵌入式 tomcat 一个信任库(他不需要)。

【问题讨论】:

    标签: java spring spring-boot tomcat ssl


    【解决方案1】:

    定义 META-INF/spring.factories 并添加 org.springframework.cloud.bootstrap.BootstrapConfiguration = ... 行

    类可能是这样的:

    @Configuration
    @BootstrapConfiguration
    public class SslConfiguration {
      @Value("${http.client.ssl.trust-store}")
      private URL trustStore;
      @Value("${http.client.ssl.trust-store-password}")
      private String trustStorePassword;
    
      @Bean
      public DiscoveryClient.DiscoveryClientOptionalArgs getTrustStoredEurekaClient(SSLContext sslContext) {
        DiscoveryClient.DiscoveryClientOptionalArgs args = new DiscoveryClient.DiscoveryClientOptionalArgs();
        args.setSSLContext(sslContext);
        return args;
      }
    
      @Bean
      public SSLContext sslContext() throws Exception {
        return new SSLContextBuilder().loadTrustMaterial(trustStore, trustStorePassword.toCharArray()).build();
      }
    }
    

    而且因为 DiscoveryClientOptionalArgs 现在定义了两次,所以再添加一个类,这个类是在 Spring Context up 之后加载的

    @Configuration
    public class DiscoveryServiceConfiguration {
      @Bean
      public static BeanFactoryPostProcessor registerPostProcessor() {
        return (ConfigurableListableBeanFactory beanFactory) -> {
          BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
          for (String beanDefinitionName : registry.getBeanDefinitionNames()) {
            if (beanDefinitionName.equalsIgnoreCase("discoveryClientOptionalArgs")) {
              BeanDefinition beanDefinition = registry.containsBeanDefinition(beanDefinitionName) ? registry.getBeanDefinition(beanDefinitionName) : null;
              if (beanDefinition != null) {
                if (registry.containsBeanDefinition(beanDefinitionName)) {
                  registry.removeBeanDefinition(beanDefinitionName);
                }
              }
            }
          }
        };
      }
    }
    

    然后它也可以工作。您不再需要定义 JVM 参数。

    【讨论】:

      【解决方案2】:

      有趣的事情 - 如果我使用嵌入式 Undertow 而不是嵌入式 Tomcat,它可以工作 - 但这是解决方案吗?

      dependencies {
        compile("org.springframework.boot:spring-boot-starter") {
          exclude module: "tomcat-embed-el"
        }
        compile("org.springframework.boot:spring-boot-starter-web") {
          exclude module: "spring-boot-starter-tomcat"
        }
        compile('org.springframework.boot:spring-boot-starter-undertow')
        ...
      }
      

      【讨论】:

        猜你喜欢
        • 2015-10-11
        • 2017-08-09
        • 2016-06-04
        • 2016-09-15
        • 2016-05-12
        • 2017-02-06
        • 1970-01-01
        • 2018-06-10
        • 2017-11-13
        相关资源
        最近更新 更多