Skip to main content
 首页 » 编程设计

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

2023年09月11日27me-sa

我想要以下针对微服务环境的加密设置。一个支持 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。