Spring Boot: Guía de RestTemplate

Aprenda a enviar solicitudes HTTP utilizando Spring RestTemplate, cómo establecer encabezados predefinidos y configurar una validación de certificado TLS mutuo.

Introducción

En esta guía, echaremos un vistazo a una de las plantillas más utilizadas y conocidas en Spring Ecosystem, conocida como RestTemplate, y cómo usar RestTemplate para enviar HTTP solicitudes, pasar encabezados predefinidos a beans RestTemplate calificados y cómo configurar verificación mutua de certificados TLS.

Spring es un marco Java popular y ampliamente difundido y evolucionó hasta convertirse en un ecosistema completo de módulos y proyectos. Spring Boot ha evolucionado a partir del proyecto Spring original y nos ayuda a arrancar fácilmente aplicaciones independientes de nivel de producción. Internamente se ocupa de una gran cantidad de código repetitivo y proporciona métodos de utilidad o anotaciones para centrarse principalmente en la lógica empresarial de la implementación.

Spring Boot emplea muchas clases de Template, como JdbcTemplate, JmsTemplate, etc., que proporcionan API simplificadas de alto nivel que realizan tareas repetitivas complejas en segundo plano. De manera similar, RestTemplate es una clase central Template que se encarga de las solicitudes HTTP síncronas como un cliente.

También incluye bibliotecas de clientes HTTP como JDK HttpURLConnection, Apache HttpComponents, etc.

Se ocupa de una gran cantidad de código repetitivo y nos permite manejar tareas comunes con facilidad, como:

  • Definición de un objeto URL
  • Abrir una conexión o un grupo de conexiones
  • Definición de un objeto de solicitud y respuesta HTTP
  • Solicitud y respuesta HTTP Marshal/Unmarshal a objetos Java
  • Manejo de excepciones de error

RestTemplate frente a WebClient

Desde Spring 5, la clase RestTemplate está en modo de mantenimiento.

Spring nos recomienda usar la clase WebClient reactiva y sin bloqueo, que ofrece escenarios de manejo de datos síncronos, asíncronos y de transmisión en lugar de RestTemplate.

Sin embargo, si está trabajando en un proyecto que se basa en una versión de Spring anterior a la 5, es mejor que use RestTemplate.

Configuración del proyecto

Hagamos girar un proyecto Spring Boot en blanco y analicemos los casos de uso y los enfoques para usar la clase RestTemplate. La forma más fácil de comenzar con un proyecto básico es a través de Spring Initializr:

Inicializar Spring

Solo agregaremos Spring Web (estamos creando una aplicación web) y Lombok (biblioteca reductora repetitiva opcional) dependencias

El enfoque principal de esta guía es cubrir el uso de RestTemplate y construir un cliente HTTP, demostrando varias solicitudes HTTP y seguridad básica. Usaremos un servicio simulado que nos permite realizar operaciones CRUD ficticias - crudcrud. Específicamente, crearemos una entidad Unicornio, que usaremos para enviar a su API.

Since we'll be sending Unicorn data over HTTP, let's create a Objeto de transferencia de datos (DTO) for it, called UnicornDTO:

1
2
3
4
5
6
// Lombok annotations for getters, setters and constructor
public class UnicornDTO {
    private String name;
    private int age;
    private String colour;
}

Para albergar las respuestas enviadas desde Crudcrud, también crearemos un objeto UnicornResponse para deserializar los datos recibidos:

1
2
3
4
5
6
7
// Lombok annotations for getters, setters and constructor
public class UnicornResponse {
    private String _id;
    private String name;
    private int age;
    private String colour;
}

Nota: ¿Necesita estas clases? Técnicamente - no. Puede enviar datos de Unicorn a través de una cadena JSON y recibir una respuesta JSON. Sin embargo, es una buena práctica tener objetos uniformes para serializar y deserializar datos.

Crear un bean RestTemplate

En cualquier Controlador, podemos instanciar directamente una instancia local de RestTemplate simplemente instanciando la clase en un objeto:

1
private RestTemplate restTemplate = new RestTemplate();

Nota: No importa mucho si la instancia es “estática” o no, ya que es segura para subprocesos. Más comúnmente, RestTemplate se define globalmente; sin embargo, existen argumentos sobre por qué querría instanciar varias instancias de RestTemplate, como cuando se trata de diferentes API, cada una con un tipo de autenticación diferente.

Si no necesita explícitamente tener varias instancias de RestTemplate ejecutándose, es mejor definirlo globalmente. De lo contrario, cada vez que la JVM llame al controlador, se creará una nueva instancia. Esta no es una buena elección de diseño desde la perspectiva del principio DRY (Don’t Repeat Yourself).

Por lo tanto, crearemos un bean en la capa Configuración y luego @Autowire este bean a cada clase Controlador para reutilizar la misma plantilla.

Además, este bean también es personalizable y podemos configurar varias opciones a través de las clases RestTemplateBuilder o RestTemplateCustomizer. Usemos RestTemplateBuilder para establecer valores de tiempo de espera para las conexiones a URL HTTP.

Para proporcionar cualquier configuración, crearemos una clase @Configuration llamada, por ejemplo, RestTemplateConfig y definiremos el bean RestTemplate de esta manera:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@Configuration
public class RestTemplateConfig {

    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder) {
    return builder
            .setConnectTimeout(Duration.ofMillis(60000))
            .setReadTimeout(Duration.ofMillis(60000))
            .build();
    }
}

Ahora podemos simplemente @Autowire el bean RestTemplate en cualquier clase para hacer solicitudes HTTP:

1
2
@Autowired
private RestTemplate restTemplate;

Habilitación del nivel de registro para mensajes de depuración {#habilitación del nivel de registro para mensajes de depuración}

Los desarrolladores tienden a implementar canalizaciones de registro eficientes para ayudar a ver los protocolos de enlace de los mensajes que se producen entre los servidores. Esta es una mina de oro de ideas y marca una gran diferencia al depurar. Desafortunadamente, Spring Boot no proporciona una forma eficiente de inspeccionar o registrar un cuerpo de respuesta JSON. Para eludir esto, intentaremos registrar encabezados HTTP o, lo que es más interesante, cuerpos HTTP como una forma de tener una visión general del intercambio de mensajes.

Una de las formas más eficientes es definir una definición de registrador en el archivo application.properties:

1
logging.level.org.springframework.web.client.RestTemplate=DEBUG

Nota: Estos registros producen mensajes bastante detallados, a menudo se recomienda deshabilitarlos en una producción ya que consumen mucha memoria en tiempo de ejecución.

If you'd like to read more about logging, read our Guía para iniciar sesión en Spring Boot.

Envío de solicitudes HTTP POST con RestTemplate

Con la configuración adecuada terminada, el registro habilitado y nuestro bean RestTemplate configurado, podemos continuar y comenzar a enviar solicitudes HTTP a través de los controladores. Comencemos con una solicitud POST, creando un recurso Unicornio ​​a través de la API de Crudcrud. Para enviar solicitudes POST, podemos usar los métodos postForEntity() o postForObject().

El método postForEntity()

El método postForEntity() acepta una cadena que indica la URL a la que estamos enviando una solicitud POST, el objeto que estamos enviando, serializado como el cuerpo HTTP y un ResponseType.

Devuelve una ResponseEntity que contiene la respuesta: una clase genérica para encapsular el código de estado de la respuesta HTTP, los encabezados HTTP y los datos devueltos. Ya que tenemos nuestra propia clase UnicornResponse, podemos envolverla dentro de una ResponseEntity:

1
2
3
4
5
6
7
8
9
@PostMapping(value = "/unicornsByEntity",
            consumes = MediaType.APPLICATION_JSON_VALUE,
            produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<UnicornResponse> createUnicornByEntity(@RequestBody UnicornDTO unicornDto) throws RestClientException, JsonProcessingException {     
    return restTemplate.postForEntity(
        "https://crudcrud.com/api/72dbefb3917c4ce1b7bb17776fcf98e9/unicorns",
        unicornDto,
        UnicornResponse.class);
}

Este controlador de solicitudes acepta una solicitud POST y deserializa su cuerpo en un objeto UnicornDTO a través de la Anotación @RequestBody, antes de usar el autowired RestTemplate para enviar este objeto al servicio Crudcrud a través del método postForEntity(), empaquetando el resultado en nuestra clase UnicornResponse y ResponseEntity que finalmente se devuelve.

Ahora, intentemos hacer una solicitud POST a nuestro controlador usando Postman. Usaremos Postman con frecuencia aquí para probar la funcionalidad de nuestras API.

If you're unfamiliar with Postman - read our guide on Primeros pasos con el cartero.

post entity resttemplate

El método postForObject()

postForObject() funciona de la misma manera que postForEntity() - la única diferencia es que postForEntity() devuelve una ResponseEntity, mientras que postForObject() devuelve ese objeto.

Para ese fin, los métodos se comportan igual, aparte de devolver un tipo diferente.

Dicho esto, acepta los mismos argumentos:

1
2
3
4
5
6
7
8
9
@PostMapping(value = "/unicornsByObject",
        consumes = MediaType.APPLICATION_JSON_VALUE,
        produces = MediaType.APPLICATION_JSON_VALUE)
public UnicornResponse createUnicornByObject(@RequestBody UnicornDTO unicornDto) throws RestClientException, JsonProcessingException {       
    return restTemplate.postForObject(
        "https://crudcrud.com/api/72dbefb3917c4ce1b7bb17776fcf98e9/unicorns",
        unicornDto,
        UnicornResponse.class);
}

Probemos rápidamente esta llamada POST en Postman:

post object resttemplate

Envío de solicitudes HTTP GET con RestTemplate

Siguiendo la misma lógica, podemos enviar solicitudes GET para obtener los recursos Unicornio ​​recién creados. Podemos usar el método getForEntity() y getForObject() para hacer esto, y siguen las mismas convenciones que las contrapartes de solicitud POST.

El método getForEntity()

El método getForEntity() devuelve un objeto ResponseEntity como respuesta, aceptando la URL del recurso y un ResponseType:

1
2
3
4
5
6
@GetMapping("/unicornsByEntity/{id}")
public ResponseEntity<String> getUnicornByIdByEntity(@PathVariable final String id) {      
    return restTemplate.getForEntity(
        "https://crudcrud.com/api/72dbefb3917c4ce1b7bb17776fcf98e9/unicorns/" + id,
        String.class);
}

Probemos esto rápidamente en Postman:

get entity resttemplate

El método getForObject()

El método getForObject() devuelve una representación del objeto como una respuesta que se encuentra en la clase ResponseType. Espera que la URL del recurso y la clase de ResponseType se pasen como parámetros:

1
2
3
4
5
@GetMapping("/unicornsByObject")
public List<UnicornResponse> getUnicornByObject() {     
    return Arrays.asList(restTemplate.getForObject("https://crudcrud.com/api/72dbefb3917c4ce1b7bb17776fcf98e9/unicorns",
        UnicornResponse[].class));
}

Probemos rápidamente esta llamada GET en Postman:

get object resttemplate

Envío de solicitudes HTTP PUT con RestTemplate

Para las solicitudes PUT, RestTemplate nos proporciona el método put(), que acepta una URL y el objeto que estamos colocando, y no devuelve ninguna respuesta:

1
2
3
4
5
6
@PutMapping(value = "/unicorns/{id}", consumes = MediaType.APPLICATION_JSON_VALUE)
public void updateUnicorn(@PathVariable final String id, @RequestBody UnicornDTO unicornDto) {
    restTemplate.put(
        "https://crudcrud.com/api/72dbefb3917c4ce1b7bb17776fcf98e9/unicorns/" + id,
        unicornDto);
}

Vamos a disparar esta solicitud a través de Postman también:

put method resttemplate

Envío de solicitudes HTTP DELETE con RestTemplate

Para solicitudes DELETE, podemos usar el método delete(). Elimina el recurso al que se dirige una URL con un ID que se pasa como parámetro. Por lo tanto, solo espera que se pase la URL y no devuelve ninguna respuesta:

1
2
3
4
@DeleteMapping("/unicorns/{id}")
public void deleteteUnicornByIdByEntity(@PathVariable final String id) {   
    restTemplate.delete("https://crudcrud.com/api/72dbefb3917c4ce1b7bb17776fcf98e9/unicorns/" + id);
}

Enviemos una solicitud de ELIMINACIÓN a través de Postman:

delete method resttemplate

El método intercambio()

Un método digno de mención es el método exchange(). Es una generalización de cualquier intercambio HTTP.

Esto significa que se puede usar para cualquier llamada HTTP y puede ser una alternativa genérica a cualquiera de las llamadas anteriores. El método exchange() devuelve una ResponseEntity y acepta una RequestEntity, que está constituida por un método HTTP, URL, encabezados y cuerpo, y un ResponseType.

Nota: También puede pasar las partes constituyentes individualmente.

El método exchange() espera que se pase como parámetros una RequestEntity o una URL, un método HTTP adecuado, una entidad HTTP serializada en un cuerpo y un objeto ResponseType.

Vamos a crear una solicitud PUT y enviarla al servicio Crudcrud, usando el método exchange() en su lugar:

1
2
3
4
5
6
7
8
@PutMapping(value = "/unicorns/{id}", consumes = MediaType.APPLICATION_JSON_VALUE)
public void updateUnicorn(@PathVariable final String id, @RequestBody UnicornDTO unicornDto) {
    restTemplate.exchange(
        "https://crudcrud.com/api/72dbefb3917c4ce1b7bb17776fcf98e9/unicorns/" + id,
        HttpMethod.PUT,
        new HttpEntity<>(unicornDto),
        Void.class);
}

Y disparemos la solicitud a través de Postman:

exchange method resttemplate

Pasar encabezados predefinidos usando RestTemplate

A menudo nos enfrentamos a situaciones en las que podríamos necesitar pasar encabezados de solicitud predefinidos para ciertas API. Estos encabezados en su mayoría se asemejan a pares o cookies de clave-valor de autenticación o autorización. También se pueden usar para establecer tipos de contenido o formatos aceptables para consumir los datos de respuesta.

Podemos intentar pasar tokens de Autenticación básica o _JWT Bearer _tokens como encabezados al llamar a una API a través de la clase RestTemplate.

En esta guía, intentaremos llamar a las API prehospedadas desde el portal API rápida COVID-19. Esta API requiere que pase obligatoriamente encabezados como "X-RapidAPI-Key" o "X-RapidAPI-Host" para obtener los últimos registros totales de Covid-19.

Para pasar estos encabezados como parte de todos los métodos que usan nuestra instancia RestTemplate, definiremos un bean dedicado sobrecargado con una implementación Interceptor. Incerceptors se utilizan incercept para agregar encabezados personalizados, registrar solicitudes o respuestas HTTP, o denegar varios tipos de solicitudes, cuando se envían o reciben.

Un interceptor común es la interfaz ClientHttpRequestInterceptor, y lo implementaremos para interceptar cualquier par clave-valor de encabezado que se pase a nuestro RestTemplate:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class HttpClientRequestInterceptor implements ClientHttpRequestInterceptor {

    private static final Logger LOGGER = LoggerFactory.getLogger(HttpClientRequestInterceptor.class);
    
    private final String headerName;

    private final String headerValue;

    public HttpClientRequestInterceptor(String headerName, String headerValue) {
        this.headerName = headerName;
        this.headerValue = headerValue;
    }

    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {      
        // Add Auth Headers
        request.getHeaders().set(headerName, headerValue);
        
        // Add logger info settings
        logRequestDetails(request);

        return execution.execute(request, body);
    }

    // Adding custom loggers
    private void logRequestDetails(HttpRequest request) {
        LOGGER.info("Request Headers: {}", request.getHeaders());
        LOGGER.info("Request Method: {}", request.getMethod());
        LOGGER.info("Request URI: {}", request.getURI());
    }
}

Ahora, podemos usar este interceptor para pasar encabezados Rapid API obligatorios a nuestro bean RestTemplate cuando sea necesario. En tales casos, RestTemplate se construirá previamente con estos encabezados.

Podemos obtener cualquier variable de entorno definida en el archivo application.properties usando la anotación @Valor, que es útil para nuestros pares clave-valor. Luego, podemos crear un bean RestTemplate personalizado y anotarlo con un Bean Name. Esto nos permite usar la anotación @Qualifier para apuntar a ese bean específico, en lugar del bean predeterminado que no tiene estos encabezados.

If you want to learn more about annotations such as these, read our guide to Anotaciones de primavera: anotaciones de marco básico.

Además, usaremos HttpComponentsClientHttpRequestFactory para establecer una configuración de tiempo de espera. Esta instancia se pasa al constructor RestTemplate y se puede usar para especificar configuraciones si aún no las tiene de un bean autocableado global.

Finalmente, usando nuestro interceptor, podemos pasar los pares clave-valor a los encabezados de cada solicitud que enviamos a través de RestTemplate:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@Value("${api.rapid-api.host}")
private String rapidApiHost;

@Value("${api.rapid-api.key}")
private String rapidApiKey;

@Bean(name = "rapidApiRestTemplate")
public RestTemplate rapidApiRestTemplate()
    throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException {
    HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory();

    // Connect timeout
    clientHttpRequestFactory.setConnectTimeout(60000);

    // Read timeout
    clientHttpRequestFactory.setReadTimeout(60000);

    RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory);

    // Interceptor section
    List<ClientHttpRequestInterceptor> interceptors = restTemplate.getInterceptors();
    if (CollectionUtils.isEmpty(interceptors)) {
        interceptors = new ArrayList<ClientHttpRequestInterceptor>();
    }
    interceptors.add(new HttpClientRequestInterceptor("x-rapidapi-key", rapidApiKey));
    interceptors.add(new HttpClientRequestInterceptor("x-rapidapi-host", rapidApiHost));
    restTemplate.setInterceptors(interceptors);

    return restTemplate;
}

application.properties tiene los pares clave-valor apropiados:

1
2
api.rapid-api.host=covid-19-data.p.rapidapi.com
api.rapid-api.key=d8ce580441msh8a191819cd7754ap111a26jsnd6df9268e190

Ahora, podemos llamar a este bean específico, en lugar de nuestro habitual RestTemplate a través de la anotación @Qualifier. Llamar al nombre del bean "rapidApiRestTemplate" autoconectará esta configuración de RestTemplate. Luego, cuando nos gustaría enviar una solicitud a un punto final, incluirán los encabezados predefinidos:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
@RestController
@RequestMapping("/api/covid")
public class CovidReportController {

    @Autowired
    @Qualifier("rapidApiRestTemplate")
    private RestTemplate restTemplate;
    
    @GetMapping("/getAllLatest")
    public ResponseEntity<String> getAllCovidCount() {      
        return restTemplate.getForEntity("https://covid-19-data.p.rapidapi.com/totals?format=json",
            String.class);
    }
}

Ahora, cuando intentamos llamar a esta API en Postman, devuelve la respuesta adecuada ya que nuestras llamadas tenían los encabezados apropiados:

Covid-19 Rapid API resttemplate

Verificación mutua de certificados TLS con RestTemplate

Hasta ahora, hemos intentado obtener datos principalmente de sitios web disponibles abiertamente, aunque estén alojados en dominios seguros. En la práctica, cuando tratamos de integrarnos con puntos finales mucho más seguros, como los que se han protegido mediante la autenticación unidireccional o mutua bidireccional, nos enfrentaremos al verdadero desafío, ya que necesitamos hacer una gran cantidad de seguridad. apretones de manos antes de que podamos obtener los datos reales.

Entonces, veamos uno de los escenarios en los que necesitamos crear un Almacén de claves y un Almacén de confianza para cifrar y agrupar los certificados del servidor y validarlos cada vez que intentamos acceder a los puntos finales.

Nota: Esta guía asume que está familiarizado con los conceptos básicos de Keystores, Truststores y capas SSL/TLS.

Como repaso rápido: se requieren Almacenes de claves y Almacenes de confianza para la comunicación y verificación SSL. Un Almacén de claves se utiliza para almacenar la clave privada del servidor y el propio certificado de identidad, mientras que un Almacén de confianza se utiliza para el almacenamiento de certificados de la Autoridad de certificación (CA) de confianza.

El servidor utiliza los almacenes de claves para las comunicaciones, mientras que los almacenes de confianza se utilizan para verificar los certificados del servidor antes de la comunicación, para permitir los protocolos de enlace.

Hagamos girar rápidamente un almacén de claves y un trustore con un simple script Bash:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#!/bin/bash

echo "generating key" 
openssl genrsa -des3 -out server.key 1024

echo "generating csr"
openssl req -new -key server.key -out server.csr

echo "generating self-signed certificate signed by key"
openssl x509 -req -days 3650 -in server.csr -signkey server.key -out server.crt

echo "generating key-store containing self-signed certificate signed by key"
openssl pkcs12 -export -in server.crt -inkey server.key -name "server" -out keystore.p12

echo "generating trust-store containing self-signed certificate signed by key"
openssl pkcs12 -export -in server.crt -inkey server.key -name "server" -out truststore.jks

Mientras ejecuta este script, se le solicitarán detalles como la frase de contraseña, el nombre común, el nombre de la organización, etc., para crear el certificado. Al final de este script, puede ver keystore.p12 y trustore.jks. El archivo .p12 indica el formato PKCS12, mientras que el archivo .jks indica el formato JKS (Java KeyStore).

Mapeemos estos archivos y tipos de archivo en nuestra application.properties:

1
2
3
4
5
6
7
application.protocol=TLSv1.2
application.keystore.path=/path/to/keystore.p12
application.keystore.type=PKCS12
application.keystore.password=passPhrase
application.truststore.path=/path/to/truststore.jks
application.truststore.type=JKS
application.truststore.password=passPhrase

La contraseña del almacén de claves y del almacén de confianza es la frase de contraseña que se proporcionó al crear los certificados y las claves del servidor. Ahora necesitamos definir otro bean RestTemplate separado y mapear estas variables para un protocolo de enlace exitoso.

Tendremos que agregar otra biblioteca, HttpClient, a nuestro proyecto, que no forma parte de Spring Initializr, a través de Maven o Gradle, para proporcionar configuraciones SSL.

Si está utilizando Maven, agregue la dependencia httpclient a su pom.xml:

1
2
3
4
5
<!--HTTP Client-->
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
</dependency>

O, si estás usando Gradle:

1
implementation group: 'org.apache.httpcomponents', name: 'httpclient'

Dado que también usaremos la biblioteca HttpClient, definamos el nivel de registro para el cliente:

1
2
logging.level.org.apache.http=DEBUG
logging.level.httpclient.wire=DEBUG

Nota: Estos registros son, como los anteriores, detallados. Evítelos en producción ya que pueden ser un acaparamiento de memoria.

Vamos a crear una instancia de Keystore y Truststore cada uno, y los agregaremos a SSLConnectionSocketFactory para su verificación. Además, usaremos un HostnameVerifier para que la fábrica pueda verificar/coincidir el Nombre común definido en el certificado con la sesión SSL realizada con el dominio real:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
@Value("${application.keystore.path}")
private String keystorePath;

@Value("${application.keystore.type}")
private String keystoreType;

@Value("${application.keystore.password}")
private String keystorePassword;

@Value("${application.truststore.path}")
private String truststorePath;

@Value("${application.truststore.type}")
private String truststoreType;

@Value("${application.truststore.password}")
private String truststorePassword;

@Value("${application.protocol}")
private String protocol;

@Bean(name = "mutualAuthRestTemplate")
public RestTemplate mutualAuthRestTemplate() throws KeyStoreException, NoSuchAlgorithmException,
    CertificateException, IOException, KeyManagementException {

    // Load Keystore
    final KeyStore keystore = KeyStore.getInstance(keystoreType);
    try (InputStream in = new FileInputStream(keystorePath)) {
        keystore.load(in, keystorePassword.toCharArray());
    }

    // Load Truststore
    final KeyStore truststore = KeyStore.getInstance(truststoreType);
    try (InputStream in = new FileInputStream(truststorePath)) {
        truststore.load(in, truststorePassword.toCharArray());
    }

    // Build SSLConnectionSocket to verify certificates in truststore
    final SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(new SSLContextBuilder()
        .loadTrustMaterial(truststore, new TrustSelfSignedStrategy()).setProtocol(protocol).build(),
        new HostnameVerifier() {
            HostnameVerifier hostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier();

            @Override
            public boolean verify(String hostname, SSLSession session) {
                return hostnameVerifier.verify(hostname, session);
            }
    });

    CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslSocketFactory).build();
    return new RestTemplate(new HttpComponentsClientHttpRequestFactory(httpclient));
}

Ahora podemos usar este Bean RestTemplate para llamar a una API REST de autenticación mutua segura usando la capa de socket TLS. Estamos utilizando TLSv1.2 como protocolo predeterminado en la implementación.

Conclusión

En esta guía, exploramos la clase RestTemplate del ecosistema Spring. Hemos echado un vistazo a cómo podemos utilizarlo para enviar solicitudes GET, POST, DELETE y PUT, así como el método genérico exchange().

También hemos sobrecargado los métodos de utilidad del cliente HTTP de Apache en un bean RestTemplate y hemos creado una capa de conexión segura para crear un protocolo de enlace SSL con diferentes puntos finales.

Puede encontrar el código fuente completo en GitHub.