Spring Boot 3.4.1:如何配置SSLBundle与RestClient协同工作?
问题
我正在使用Spring Boot 3.4.1、JDK 23和MySQL 9,对Spring Framework新版本的RestClient特性很感兴趣。之前已经通过RestTemplate成功实现了双向TLS(mutualTLS),现在希望在RestClient中配置SSLBundle(若无法实现则使用SSLContext)。
以下是我的配置文件和代码:
application.yml
server: port: 8001 ssl: bundle: bar client-auth: NEED enabled: true spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/fuu001?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true username: ggAABB password: fg33435 ssl: bundle: jks: foo: keystore: location: C:\\ssl\\server-identity.p12 password: fdfd type: PKCS12 truststore: location: C:\\ssl\\server-truststore.p12 password: fg54533 type: PKCS12 reload-on-update: true bar: keystore: location: C:\\ssl\\client-identity.p12 password: dfdsfs type: PKCS12 truststore: location: C:\\ssl\\client-truststore.p12 password: 4573d@ type: PKCS12 reload-on-update: true jpa: hibernate: ddl-auto: create-drop properties: hibernate: format_sql: true show-sql: true # security: # user: # name: user # password: g5643544
CallFooService.java
package com.example; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.stereotype.Service; import org.springframework.web.client.RestClient; import org.springframework.web.client.RestTemplate; import java.util.Map; @Service public class CallFooService { @Autowired private RestTemplate restTemplate; public void testMethod(){ String response = restTemplate.getForObject("https://localhost:8001/user", String.class); System.out.println("response = " + response); String response2 = restTemplate.getForObject("https://localhost:8002/user", String.class); System.out.println("response2 = " + response2); RestClient defaultClient = RestClient.create(); RestClient customClient = RestClient.builder() .requestFactory(new HttpComponentsClientHttpRequestFactory()) // 我想在此处注入SSLBundle或SSL Context .messageConverters(converters -> converters.add(null)) .baseUrl("https://example.com") .defaultUriVariables(Map.of("variable", "foo")) .defaultHeader("My-Header", "Foo") .defaultCookie("My-Cookie", "Bar") .requestInterceptor(null) .requestInitializer(null) // 我想在此处注入SSLBundle或SSL Context .build(); } }
请问如何在Spring Boot 3.4.1中配置SSLBundle与RestClient协同工作?
解决方案
在Spring Boot 3.4.1中,你可以直接复用已配置的SSLBundle来构建支持双向TLS的RestClient,步骤如下:
1. 注入目标SSLBundle
Spring Boot会自动将配置文件中定义的SSLBundle注册为Bean,你可以通过@Autowired结合@Qualifier注入指定的bundle(比如foo或bar):
@Autowired @Qualifier("foo") // 替换为你需要使用的bundle名称,比如"bar" private SSLBundle sslBundle;
2. 基于SSLBundle构建请求工厂
RestClient需要配置ClientHttpRequestFactory来启用SSL,你可以通过以下几种方式将SSLBundle集成进去:
方式一:使用SimpleClientHttpRequestFactory(轻量实现)
直接从SSLBundle提取SSLContext和HostnameVerifier,配置到SimpleClientHttpRequestFactory:
// 从SSLBundle获取SSL上下文和主机名验证器 SSLContext sslContext = sslBundle.createSslContext(); HostnameVerifier hostnameVerifier = sslBundle.getHostnameVerifier(); SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory(); requestFactory.setSslContext(sslContext); requestFactory.setHostnameVerifier(hostnameVerifier);
方式二:使用HttpComponentsClientHttpRequestFactory(Apache HttpClient实现)
如果需要更复杂的HTTP配置(比如连接池),可以结合Apache HttpClient:
SSLContext sslContext = sslBundle.createSslContext(); // 构建支持SSL的HttpClient CloseableHttpClient httpClient = HttpClientBuilder.create() .setSSLContext(sslContext) .setSSLHostnameVerifier(sslBundle.getHostnameVerifier()) .build(); HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
方式三:使用Spring工具类快速构建(推荐)
Spring Boot提供了ClientHttpRequestFactories工具类,可以直接从SSLBundle生成请求工厂,简化代码:
ClientHttpRequestFactory requestFactory = ClientHttpRequestFactories.get( ClientHttpRequestFactories.Options.sslBundle(sslBundle) );
3. 构建配置好SSL的RestClient
将上面生成的requestFactory传入RestClient.builder即可:
RestClient customClient = RestClient.builder() .requestFactory(requestFactory) .baseUrl("https://localhost:8002") // 替换为目标服务地址 .defaultHeader("My-Header", "Foo") .defaultCookie("My-Cookie", "Bar") // 其他配置保持不变 .build();
关键说明
- 复用Spring Boot的SSLBundle配置后,无需手动加载证书文件,且配置中开启的
reload-on-update: true会自动生效,证书更新时无需重启应用。 - 双向TLS的验证逻辑完全由SSLBundle中的keystore(客户端身份)和truststore(信任的服务端证书)控制,和你之前RestTemplate的配置逻辑一致。
内容的提问来源于stack exchange,提问作者Đỗ Như Vỹ

