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
文件夹:
接下来,在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
文件夹中:
接着,在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()
方法创建它。
然后,我们使用SSLContextBuilder
的loadTrustMaterial()
方法将keystore文件和凭据加载到SSLContext
对象中。
接着,我们创建SSLConnectionSocketFactory
,这是一个用于TLS和SSL连接的分层套接字工厂,通过加载SSLContext
来验证服务器是否使用了我们在上一步中加载的可信证书列表,即对服务器进行身份验证。
现在,我们可以使用定制的RestTemplate
在端点http://localhost:8082/welcomeclient:
上消费受保护的REST服务:
4. 总结
在这篇文章中,我们讨论了如何使用定制的RestTemplate
来消费受保护的REST服务。
如往常一样,源代码可以在GitHub上找到。