Tests de integración con Spring Boot + Spring Security en servicios REST

2
16864

En este tutorial vamos a ver cómo hacer Tests de integración con Spring Boot y Spring Security en servicios REST

0. Índice de contenidos

1. Introducción

Combinando herramientas como spring-boot, spring-security y spring-mvc pueden ofrecernos soluciones muy sencillas de desarrollar y que cubren muchas de las requisitos comunes
de hoy día, como puede ser, aplicar seguridad a la capa de servicios REST de nuestra aplicación (aunque hoy día se está externalizando la seguridad de servicios en herramientas de apoyo, como un API Manager. Más info aquí).

Este tutorial tiene dos objetivos principales:

  • Veremos cómo implementar seguridad en servicios REST a través de configuración Spring basado en Java con Spring Security. La aplicación que contendrá dicho servicio estará desarrollada con spring-boot. Para más información sobre spring-boot, puedes consultar este tutorial
  • Desarrollaremos tests de integración con spring-boot y TestRestTemplate sobre nuestro servicio, y veremos cómo aplicar la seguridad básica en las peticiones de forma sencilla.

2. Entorno

  • Macbook pro core i7 con 16gb RAM
  • SO: Yosemite
  • IDE: IntelliJ IDEA 14
  • Gestión de dependencias: Apache Maven 3.3.3
  • Java: JDK 1.8
  • Spring: 4.1.6.RELEASE
  • Spring-boot: 1.2.5.RELEASE
  • Spring-security: 3.2.2.RELEASE

3. Aplicación spring-boot con Spring security

Lo primero que vamos a hacer es configurar nuestra aplicación con spring-boot, y luego le aplicaremos una capa de seguridad básica con autenticación usuario/contraseña.
Se muestra a continuación el código y luego daremos una explicación de cada elemento. Nuestra aplicación estará configurada en una clase Application.java con el siguiente contenido:

Expliquemos el código por línea:

  • Línea 1. @SpringBootApplication: Esta anotación indica que se trata de una aplicación basada en spring-boot
  • Línea 2. @EnableAutoConfiguration: Anotación de spring-boot que incluye beans recomendados dependiendo de las dependencias que tengas en tu proyecto. Es decir, que si añades una dependencia tomcat-embedded.jar, te creará una instancia de TomcatEmbeddedServletContainerFactory, porque es posible que la uses. Es parte de la «magia» que hace spring-boot por tí
  • Línea 3. @EnableWebSecurity: Anotación de spring-security que indica a la configuración de Spring que se van a redefinir métodos de las clases WebSecurityConfigurer o de WebSecurityConfigurerAdapter (que es la que usaremos en este tutorial)
  • Línea 4. @ComponentScan(..): Anotación de spring para la búsqueda de componentes por paquetes.
  • Línea 5. Nuesta aplicación extiende de WebSecurityConfigurerAdapter para redefinir los métodos de seguridad necesarios. En este caso sólo implementaremos seguridad básica.
  • Línea 8. Se redefine este método para aplicar autenticación básica a nuestra aplicación. Servirá para todas las llamadas HTTP que se realicen sobre el contexto de la misma
  • Línea 12. Este es el main que será ejecutado desde el test de integración

Con esto ya podemos levantar una aplicación bajo un contenedor de servlet (por defecto Tomcat 8), y con seguridad básica usuario/password implementada. Las posibilidades de configuración de seguridad a nivel de aplicación,
mediante el uso de WebSecurityConfigurerAdapter son muy amplias (de hecho puedes configurar la mayoría de aspectos que te proporciona spring-security).

No hemos necesitado hacer uso de spring-security 4 para realizar esta solución. No obstante, spring-security 4 nos ofrece bastantes funcionalidades nuevas muy interesantes. Os dejo el enlace a su documentación (v4.0.1.RELEASE) aquí.

4. Definición del servicio REST

Para esta solución hemos implementado un servicio REST muy sencillo, con un sólo método GET, que obtiene información del bean Persona. Dicho bean lo podemos ver a continuación:

Y el controlador Spring se muestra a continuación:

Como podéis ver, se trata de un servicio muy sencillo que almacena una lista de dos personas, simulando información que viene de, por ejemplo, una base de datos.

En el servicio cabe destacar lo siguiente:

  • Línea 1. @RestController: Anotación que combina @Controller con @ResponseBody en los métodos del servicio.
  • Línea 15.@AuthenticationPrincipal: Anotación de spring-security que indica que el parámetro de tipo Principal se inyectará con la información del usuario que hace la petición contra el servicio
  • Línea 30. Manejador de excepción PersonException en caso de lanzar una petición con una persona que no existe

El servicio devolverá un 200(OK) si se encuentra la persona (y devolverá la misma en formato JSON en el body de la respuesta), o un 404(Not Found) si no se encuentra.

5. Test de integración

Además de un test unitario (que no muestro en el tutorial, y que os lo podéis descargar del repositorio Git (ver Configuración y código fuente)) se ha desarrollado un test de integración.
Gracias a la integración con spring-boot, disponemos de una serie de clases y anotaciones de utilidad que nos servirán para desarrollar tests de integración de forma sencilla.
En concreto, cuando estamos desarrollando este tipo de tests con spring-boot, se realizan las siguientes acciones:

  1. Se levanta el contenedor de servlets (por defecto Tomcat 8)
  2. Se levanta el contexto de Spring
  3. Se ejecutan los tests de la clase de Test (ojo! ¡y de todos los tests de integración del proyecto! Se comparte el contenedor que se ha levantado 😉
  4. Se le manda señal de shutdown al contenedor de servlets

Nuestro test de integración al servicio REST queda como sigue:

Vamos a explicarlo línea a línea:

  • Línea 2. @SpringApplicationConfiguration(..): Seguramente nos recuerde a @ContextConfiguration(..), para tests de integración con Spring: enlace. Indica la configuración de la aplicación spring-boot; Le indicaremos la clase de la aplicación que contiene el main de spring-boot.
  • Línea 3. @WebIntegrationTest(randomPort = true): Esta es una de las partes más interesantes del test de integración. Esta anotación de spring-boot indica que se trata de un test de integración sobre una aplicación real.
    Implementa un bootstrapper (WebAppIntegrationTestContextBootstrapper para ser más exactos) que permite, entre otras cosas, capturar el puerto donde se ha levantado el contenedor de servlets, de forma que lo podamos caputar en el test
    (mediante un @Value(..), por ejemplo) y lo podamos usar para hacer las pruebas contra el servicio. A la hora de levantar el contenedor de servlets se le puede indicar si queremos que se levante en algún puerto específico, o en un puerto (libre) aleatorio.
  • Línea 6. Aquí se inyecta el valor de la propiedad local.server.port, que indica el puerto donde se ha levantado la aplicación spring-boot.
  • Línea 9. TestRestTemplate: Clase de spring-boot que extiende del clásico RestTemplate, y que implementa funcionalidad de autenticación básica contra servicios securizados. Usarlo es tan sencillo como añadir el usuario y la contraseña como parámetros del constructor, de forma que irán en las cabeceras de cada una de las peticiones que se realicen contra el servicio que consume.

Con esto tenemos un test de integración atacando al servicio que acabamos de implementar. Éste test estará dentro del ciclo de vida de Maven gracias al plugin maven-failsafe-plugin. Si ejecutamos mvn integration-test, en la consola se muestra lo siguiente:

6. Configuración y código fuente

El fichero de configuración de Maven tendrá esta estructura:

El código del proyecto lo podéis encontrar aquí

7. Conclusiones

Con el desarrollo que se expone en este tutorial se pueden realizar soluciones de integración de componentes independientes dentro de una aplicación empresarial,
como puede ser la definición de una capa de servicios REST.
Las posibilidades que nos ofrece spring-boot para la definición de este tipo de componentes nos ahorra mucho tiempo de desarrollo,
y las clases de utilidad que nos ofrece este módulo de Spring nos permiten encapsular funcionalidad que de otra forma nos llevaría a escribir muchas líneas de código.
Además, nos permiten definir tests independientes del entorno, independientes del servidor de aplicaciones donde esté corriendo la aplicación (ya que levanta uno propio embebido), o configuración según entorno.

Una consideración que hay que tener en cuenta es que estos tests no soportan transaccionalidad (mediante @Transactional), aunque sí si le inyectamos directamente el transactionManager al test.

Por último, existe un módulo específico de testing de spring-boot: spring-boot-starter-test. Dicho módulo provee de algunas herramientas útiles para testing;
por ejemplo, te incluye las librerías mockito, hamcrest, spring-test, junit, y algunas clases de apoyo para testing. En este tutorial se ha optado por incluir las librerías de forma independiente, pero esta es otra opción igual de válida.
Échale un vistazo al apartado de referencias para saber más de este módulo de spring-boot.

8. Referencias

  • Spring security project: enlace
  • Enable web security in Spring projects: enlace
  • Spring boot test: enlace
  • Spring MVC Rest services: enlace

2 Comentarios

Dejar respuesta

Please enter your comment!
Please enter your name here