1. 概述

在这个教程中,我们将学习如何使用Spring的RestTemplate来消费一个受HTTPS保护的REST服务。

2. 准备工作

我们知道,为了保护REST服务,我们需要一个证书和由证书生成的keystore。 在生产级应用中,我们可以从认证机构(CA)获取证书以确保应用程序的安全性和信任性。

本文的目的在于,我们在示例应用中使用自签名证书。

我们将使用Spring的RestTemplate来访问HTTPS REST服务。

首先,创建一个控制器类WelcomeController,并添加一个返回简单字符串响应的/welcome端点:

@RestController
public class WelcomeController {

    @GetMapping(value = "/welcome")
    public String welcome() {
       return "Welcome To Secured REST Service";
    }
}

然后,将keystore放入src/main/resources文件夹:

keystore资源文件夹

接下来,在application.properties文件中添加与keystore相关的属性:

server.port=8443
server.servlet.context-path=/
# The format used for the keystore
server.ssl.key-store-type=PKCS12
# The path to the keystore containing the certificate
server.ssl.key-store=classpath:keystore/baeldung.p12
# The password used to generate the certificate
server.ssl.key-store-password=password
# The alias mapped to the certificate
server.ssl.key-alias=baeldung

现在,我们可以通过这个端点访问REST服务:https://localhost:8443/welcome

3. 消费受保护的REST服务

Spring提供了方便的RestTemplate类来调用REST服务。虽然消费简单的REST服务相对直接,但消费受保护的服务时,我们需要根据服务使用的证书/keystore定制RestTemplate

3.1. 创建RestTemplate客户端

让我们编写一个简单的控制器,使用RestTemplate来消费我们的REST服务:

@RestController
public class RestTemplateClientController {
    private static final String WELCOME_URL = "https://localhost:8443/welcome";

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/welcomeclient")
    public String greetMessage() {
        String response = restTemplate.getForObject(WELCOME_URL, String.class);
        return response;
    }
}

如果我们运行代码并访问/welcomeclient端点,会收到错误,因为找不到访问受保护REST服务的有效证书:

javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)

接下来,我们将解决这个问题。

3.2. 配置RestTemplate以访问HTTPS

客户端应用访问受保护的REST服务时,其resources文件夹应包含一个安全的keystore。此外,RestTemplate本身也需要进行配置。

首先,将先前的keystore baeldung.p12作为信任store添加到src/main/resources文件夹中:

keystore在资源文件夹中

接着,在application.properties文件中添加信任store的详细信息:

server.port=8082
#trust store location
trust.store=classpath:keystore/baeldung.p12
#trust store password
trust.store.password=password

最后,定制RestTemplate,添加信任store:

@Configuration
public class CustomRestTemplateConfiguration {

    @Value("${trust.store}")
    private Resource trustStore;

    @Value("${trust.store.password}")
    private String trustStorePassword;

    @Bean
    public RestTemplate restTemplate() throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException, CertificateException, MalformedURLException, IOException {

        SSLContext sslContext = new SSLContextBuilder()
         .loadTrustMaterial(trustStore.getURL(), trustStorePassword.toCharArray()).build();
        SSLConnectionSocketFactory sslConFactory = new SSLConnectionSocketFactory(sslContext);
        HttpClientConnectionManager cm = PoolingHttpClientConnectionManagerBuilder.create()
                .setSSLSocketFactory(sslConFactory)
                .build();
        CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(cm).build();
        ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
        return new RestTemplate(requestFactory);
    }
}

让我们详细理解上面restTemplate()方法中的关键步骤。

首先,我们创建一个SSLContext对象,它代表安全套接字协议实现。我们使用SSLContextBuilder类的build()方法创建它。

然后,我们使用SSLContextBuilderloadTrustMaterial()方法将keystore文件和凭据加载到SSLContext对象中。

接着,我们创建SSLConnectionSocketFactory,这是一个用于TLS和SSL连接的分层套接字工厂,通过加载SSLContext来验证服务器是否使用了我们在上一步中加载的可信证书列表,即对服务器进行身份验证。

现在,我们可以使用定制的RestTemplate在端点http://localhost:8082/welcomeclient:上消费受保护的REST服务:
使用定制`RestTemplate`消费受保护REST服务的响应

4. 总结

在这篇文章中,我们讨论了如何使用定制的RestTemplate来消费受保护的REST服务。

如往常一样,源代码可以在GitHub上找到。