如何在 Sping Boot 3 中配置使用 Apache HttpClient 5 的 RestTemplate,并让它在进行 SSL/TLS 连接时忽略证书

1,876 阅读3分钟

如果你需要在 Spring Boot 3 中配置使用 Apache HttpClient 5 的 RestTemplate,并让它在进行 SSL/TLS 连接时忽略证书验证(例如,连接使用自签名证书或无效证书的 HTTPS 端点),你需要创建一个自定义的 SSLContext,该 SSLContext 信任所有证书,并可能禁用主机名验证。

请务必注意:忽略 SSL 证书验证会带来严重的安全风险,因为它使得你的应用程序容易受到中间人攻击 (Man-in-the-Middle, MitM)。这种配置绝对不应该在生产环境中使用,只应在受控的开发或测试环境中使用,并且明确知晓相关风险。

以下是实现步骤:

1. 添加 HttpClient 5 依赖

确保你的 pom.xml (Maven) 或 build.gradle (Gradle) 包含 HttpClient 5 依赖:

Maven (pom.xml):

<dependency>
    <groupId>org.apache.httpcomponents.client5</groupId>
    <artifactId>httpclient5</artifactId>
    <!-- Spring Boot 通常会管理版本 -->
</dependency>

Gradle (build.gradle):

implementation 'org.apache.httpcomponents.client5:httpclient5'

2. 创建配置类和 RestTemplate Bean

在你的 Spring Boot 应用中创建一个 @Configuration 类来定义 RestTemplate Bean。

import org.apache.hc.client5.http.classic.HttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
import org.apache.hc.client5.http.io.HttpClientConnectionManager;
import org.apache.hc.client5.http.ssl.NoopHostnameVerifier;
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactoryBuilder;
import org.apache.hc.client5.http.ssl.TrustAllStrategy; // 使用 HttpClient 5 自带的 TrustAllStrategy
import org.apache.hc.core5.ssl.SSLContexts;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

import javax.net.ssl.SSLContext;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;

@Configuration
public class InsecureRestTemplateConfig {

    @Bean
    public RestTemplate insecureRestTemplate(RestTemplateBuilder builder) {
        try {
            // 1. 创建信任所有证书的 SSLContext
            // 使用 HttpClient 5 提供的 TrustAllStrategy
            SSLContext sslContext = SSLContexts.custom()
                    .loadTrustMaterial(null, TrustAllStrategy.INSTANCE) // 关键:加载信任所有策略
                    .build();

            // 2. 创建 SSLConnectionSocketFactory,允许所有主机名
            // NoopHostnameVerifier.INSTANCE 禁用主机名验证
            SSLConnectionSocketFactory sslSocketFactory = SSLConnectionSocketFactoryBuilder.create()
                    .setSslContext(sslContext)
                    .setHostnameVerifier(NoopHostnameVerifier.INSTANCE) // 关键:禁用主机名验证
                    .build();

            // 3. 创建连接管理器,并设置 SSL Socket Factory
            HttpClientConnectionManager connectionManager = PoolingHttpClientConnectionManagerBuilder.create()
                    .setSSLSocketFactory(sslSocketFactory)
                    .build();

            // 4. 创建 HttpClient
            CloseableHttpClient httpClient = HttpClients.custom()
                    .setConnectionManager(connectionManager)
                    // 可选:禁用重定向、重试等
                    // .disableAutomaticRetries()
                    // .disableRedirectHandling()
                    .build();

            // 5. 创建 HttpComponentsClientHttpRequestFactory
            HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
            requestFactory.setHttpClient(httpClient);

            // 可选:设置连接和读取超时
            // requestFactory.setConnectTimeout(5000); // 5 seconds
            // requestFactory.setReadTimeout(30000); // 30 seconds

            // 6. 使用 RestTemplateBuilder 构建 RestTemplate
            return builder
                    .requestFactory(() -> requestFactory)
                    .build();

        } catch (NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) {
            // 在启动时配置失败是严重问题,通常应抛出异常
            throw new RuntimeException("Failed to create insecure RestTemplate", e);
        }
    }
}

代码解释:

  1. SSLContexts.custom().loadTrustMaterial(null, TrustAllStrategy.INSTANCE).build(): 这是创建信任所有证书的 SSLContext 的核心部分。
    • loadTrustMaterial(null, ...) 表示我们不加载任何特定的信任库文件。
    • TrustAllStrategy.INSTANCE 是 Apache HttpClient 5 提供的一个预定义的 TrustStrategy,它的 isTrusted 方法总是返回 true,从而信任任何证书链。
  2. NoopHostnameVerifier.INSTANCE: 这是 Apache HttpClient 5 提供的一个 HostnameVerifier,它不执行任何主机名验证。这意味着即使 SSL 证书中的主机名与你实际连接的服务器主机名不匹配,连接也会被允许。禁用主机名验证同样是危险的,因为它使得更容易受到 DNS 欺骗等攻击。
  3. SSLConnectionSocketFactoryBuilder: 用于构建配置了自定义 SSLContextHostnameVerifierSSLConnectionSocketFactory
  4. PoolingHttpClientConnectionManagerBuilder: 用于构建连接管理器。我们通过 .setSSLSocketFactory(sslSocketFactory) 将自定义的 SSL 配置应用到管理器。使用连接池是推荐的做法。
  5. HttpClients.custom().setConnectionManager(connectionManager).build(): 创建最终的 CloseableHttpClient 实例,并将配置好的连接管理器设置给它。
  6. HttpComponentsClientHttpRequestFactory: Spring 的适配器类,它包装了 Apache HttpClient,使其能被 RestTemplate 使用。我们将上面创建的 httpClient 设置给它。
  7. RestTemplateBuilder: Spring Boot 推荐的方式来构建 RestTemplate。我们通过 .requestFactory(() -> requestFactory) 将自定义的工厂提供给构建器。

如何使用:

现在你可以在你的服务或组件中注入这个特殊配置的 RestTemplate Bean:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

@Service
public class MyService {

    private final RestTemplate insecureRestTemplate;

    // 使用 @Qualifier 指定注入我们自定义的 Bean 名称 (insecureRestTemplate)
    @Autowired
    public MyService(@Qualifier("insecureRestTemplate") RestTemplate insecureRestTemplate) {
        this.insecureRestTemplate = insecureRestTemplate;
    }

    public String callInsecureEndpoint(String url) {
        // 这个 RestTemplate 会忽略 SSL 证书错误
        try {
            return insecureRestTemplate.getForObject(url, String.class);
        } catch (Exception e) {
            // 处理调用异常
            System.err.println("Error calling insecure endpoint: " + e.getMessage());
            return null;
        }
    }
}

再次强调:

  • 极度不安全: 这种配置绕过了 SSL/TLS 的核心安全机制。
  • 仅限测试/开发: 只在完全理解风险并能控制环境的情况下使用。
  • 不要用于生产: 绝对不要在生产代码中使用此配置。生产环境应始终验证 SSL 证书。

如果你需要信任特定的自签名证书,更好的方法是将其导入到一个自定义的信任库 (TrustStore) 中,并配置 HttpClient 使用该信任库,而不是完全禁用验证。

扫码_搜索联合传播样式-标准色版.png