java - SSL环境下的SpringBoot+Eureka+CloudConfig

标签 java spring spring-boot tomcat ssl

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

所以...我做了什么:

Eureka 发现服务器,包括/config/* 上的云配置服务 服务器正在将自己注册到 Eureka 服务器,Eureka 仪表板显示该实例。此服务在启用 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 服务器并从配置服务器加载配置。所以我将 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,因为我处于 Bootstrap 中(至少我发现没有任何作用)。上述带有 SSLConfiguration 的解决方案也不起作用。

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

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 args,所以我们不应该再使用 SSLConfiguration。已删除。

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

如果我禁用云配置 - 嵌入式 Tomcat 仍然有同样的错误。如果我随后也删除 JVM args - 服务出现。

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

最佳答案

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

类(class)可能是这样的:

@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 args。

关于java - SSL环境下的SpringBoot+Eureka+CloudConfig,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50972390/

相关文章:

java - JVM 进程如何分配内存?

java - Java中的线程概念

Spring REST - 必须至少有一个 RequestParameter

java - 不允许 IntentReceiver 组件绑定(bind)到服务

java - 将自己的方法应用于 h2

java - Spring Boot 错误 @Autowired RestTemplateBuilder 与 junit

spring-boot - Spring Boot Social/connect/twitter 与/signin/twitter

java - 在 Spring Boot 中将 @Bean 放在哪里?

java - 未找到 util.Random 符号

java - 如何将 @Qualifier 与服务和存储库一起使用