- :)
- :0
- :D
- ;)
- :]
- :º
Aprende a realizar una aplicación web con Java y Springboot de forma segura y lista para llevarla a producción
Artículo Técnico
SpringBoot se ha convertido en el framework de Java más usado en el mundo, por encima de los estándares J2EE. A continuación explicamos los motivos de porqué es el conjunto de librerías más usado y porqué conviene usarlo al empezar cualquier proyecto web.
Ventajas del uso de SpringBoot
SpringBoot es gratis
SpringBoot es un módulo del proyecto de Spring que fue creado para simplificar el desarrollo de aplicaciones con Spring Framework bajo licencia Apache 2.0. Pone a nuestro alcance una infraestructura para el desarrollo de aplicaciones en una plataforma de lenguaje Java de código abierto, que hace mucho más fácil la vida de los programadores ahorrando tiempo y costes, sin por ello sacrificar control sobre el código ni rendimiento.
Aunque el proyecto de Spring sale a la luz en 2003, es en 2014 donde se puede ver su versión 1.0 del modulo de SpringBoot, hasta hoy día que tenemos la versión 2.2.5 released con soporte para Java 13.
Spring Boot funciona sobre la JVM
Una de las grandes ventajas de trabajar sobre JVM, es que permite desarrollar nuevas aplicaciones, migrar o reutilizar código de cualquier proyecto ya desarrollado en Java, Kotlin o Grovy y ademas sobre uno de los entornos más extendido y potente en estos días como lo es JVM, con el soporte que esto significa.
La máquina virtual habilita a cualquier aplicación Spring, y Java en general, a ser ejecutada en los principales sistemas operativos, tanto de servidores, como máquinas virtuales o en computadoras personales.
SpringBoot permite hacer Webs y Microservicios
Actualmente hay una alta tendencia empresarial a migrar las aplicaciones sobre una arquitectura de desarrollo de microservicios, o Startups que directamente utilizan esta arquitectura por su flexibilidad gracias a la separación de responsabilidades en microservicios independientes, que permiten entrar y/o migrar entre los diferentes modelos de negocio. La separación en microservicios permite realizar aplicaciones especializadas y fácilmente reutlizables dando mucha potencia y flexibilidad a los sistemas de información.
Esta gran versatilidad de SpringBoot hace que podamos abarcar prácticamente cualquier reto en el ámbito profesional de la programación con su ayuda.
Al mismo tiempo puede usarse Spring Boot para realizar aplicaciones web tradicionales o multi página en donde el HTML es generado dinámicamente en el lado del servidor, antes de ser entregado al navegador web del usuario.
Spring Boot es rápido
Imagínate no tener que configurar contenedores ni servidores web Apache Tomcat, tener la estructura de tu aplicación y autoconfigurado tu framework Spring, tener todas tus dependencias en un formato legible en un archivo POM para su gestión Maven, además de la conexión a tu base de datos, todo en cinco minutos. No es ninguna exageración, el automatismo que ofrece SpringBoot te permite empezar a desarrollar prácticamente de inmediato y desplegar tu aplicación sin complicaciones, sin sacrificar nada a cambio.
Con Spring Boot es rápido desarrollar y es rápido de ejecutar. Apoyado en todo el framework de desarrollo Spring, las aplicaciones hechas con esta tecnología está optimizadas para altas cargas de trabajo con un mínimo consumo de memoria, gracias a que la mayoría de entidades son singletons y por tanto objetos java reutilizados entre threads. Esta aproximación mantiene mínima la memoria RAM necesaria por cada cliente simultáneo, convirtiéndolo en sistemas óptimos para entornos web con miles de peticiones por segundo.
SpringBoot tiene un gran ecosistema
Spring, nacido en 2003, se hizo famoso en la comunidad de programadores Java en parte gracias a una documentación bien estructurada y completa, por encima de la media general que solemos ver en proyectos de código abierto y que a día de hoy aún agradecen.
En 2004 los desarrolladores formaron su propia compañía y surgió The Spring Forum, el foro de la comunidad de desarrolladores con Spring que dió a conocer aún más el framework, sin dejar de crecer y convertirse en la fuente de información más importante. Sólo en sus inicios, ya se reunían más de 300 programadores en conferencias de Spring.
SpringBoot tiene un gran número de programadores
En 2017 un blog de la web jRebel publicó un gráfico del índice de uso de diferentes frameworks de Java, basándose en los servicios de LinkedIn, StackOverflow, GitHub y búsquedas sobre cuestiones específicas de Spring en el motor de Google. De entre los 17 frameworks analizados, Spring MVC quedó el primero seguido por JSF que una diferencia notable: Java Web Frameworks index
Spring Boot viene con baterías incluidas
Spring es todo un marco de trabajo completo para desarrollar aplicaciones empresariales. Tiene todo lo necesario para abarcar las funcionalidades esperadas de cualquier aplicación profesional. Desde gestión de configuraciones, seguridad, capa de acceso a datos SQL y noSQL, inyección de depencencias, monitorización, testing, etc…
La documentación del proyecto es una de las mejores del mundo Open Source y cualquier usuario encontrará la información con la que poder usar cualquiera de sus módulos, que se mantienen actualizados con cada versión y con cada recomendación de la W3C para el desarrollo de aplicaciones web seguras.
Spring Boot es políglota
Gracias al uso de la JVM, se amplia el abanico de posibilidades de integraciones entre diferentes lenguajes de programación, bases de datos, etc… dando la oportunidad de utilizar la tecnología que mas convenga a cada modelo de negocio.
SpringBoot tiene soporte de las principales empresas
Al estar basado en la plataforma JVM disfruta de la fama y apoyo de todos los proyectos y grandes empresas que hacen uso de esta y de las que en particular hacen uso de Spring como son: MIT, Intuit, PedidosYa, Trivago, MercadoLibr, Google Cloud…
SpringBoot está en continua evolución
La idea de Spring es que como programador te centres en desarrollar tu aplicación y no malgastes tiempo ni recursos en procesos repetitivos ni configuraciones.
El ecosistema de Spring es un ecosistema de módulos que podemos añadir a nuestra aplicación según necesitemos o no para conseguir este fin. Podemos mencionar por importancia y funcionalidad: Spring Data (simplificación de acceso a los datos), Spring Security (autenticación y seguridad) y SpringBoot (autoconfiguración y despliegue) que es uno de los módulos que se vuelve mas imprescindible.
En la actualidad podemos ver en la página oficial de Spring como tienen en continua evolución 25 proyectos y 7 proyectos más para su futuro desarrollo: Spring Projects.
Cómo crear una aplicación con SpringBoot Initializr
Aquí tienes una guía paso a paso de cómo crear un proyecto en blanco, como plantilla para comenzar cualquier desarrollo web. En este tutorial utilizamos Java y Spring Boot para construir una aplicación web desde cero con sencillos pasos usando SpringBoot Initializr.
Por parte de los desarrolladores de Spring, se tiene a disposición una herramienta web online denominada Spring Initializr donde por medio de unos parámetros de configuración genera automáticamente un proyecto Maven o Gradle, según se elija, en un archivo comprimido Zip conteniendo la carpeta con la estructura de la aplicación para ser importada directamente desde el editor de programación como Eclipse IDE, Netbeans IDE o Intellij.
Se puede acceder mediante el enlace a la dirección: https://start.spring.io/
El asistente web solicita una serie de datos necesarios para poder ejecutar la plantilla que construye los primeros archivos del programa. Para todos ellos aporta una configuración por defecto que conviene cambiar, como el nombre de la aplicación, o el package que se usará en las clases generadas.
A continuación se explica qué parámetros hay y para qué sirven:
- Project: Permite elegir la herramienta de construcción de la aplicación. En Java las dos herramientas más usadas son Maven y Gradle. Recomendamos Maven al ser la más expendida.
- Language: Lenguaje de programación que se va a utilizar en la aplicación. Los tres tipos están soportados por la máquina virtual JVM. Java es la opción más extendida y tiene mejor soporte de los editores de programación.
- Spring Boot: Versión del Spring Boot a usar. Siempre que se pueda se optará por la última estable, compuesta únicamente por números.
- Project Metadata, Group: Se refiere al descriptor de Maven groupId, utilizado para clasificar el proyecto en los repositorios de binarios. Normalmente se suele usar una referencia similar a la de los packages de las clases. Por ejemplo, com.arteco.web para disponer todas las aplicaciones web en el mismo directorio.
- Project Metadata, Artifact: Se refiere al otro descriptor de Maven artifactId, y por tanto para indicar el nombre del proyecto y del binario resultante. La combinación de groupId y artifactId (más la versión) identifican inequívocamente a un binario dentro de cualquier organización.
- Packaging: Indica qué tipo de binario se debe construir. Si la aplicación se ejecutará por sí sola se seleccionará JAR, éste contiene todas las dependencias dentro de él y se podrá ejecutar con java -jar binario-version.jar. Si por el contrario, la aplicación se ejecutará en un servidor J2EE existente o en un Tomcat ya desplegado se deberá escoger WAR.
- Java: Se selecciona la versión de Java a usar. En este caso, se recomienda usar la versión de Java más antigua para garantizar la compatibilidad con otras librerías o proyectos que se quieran incluir, así será más probable encontrar documentación existente que siga siendo válida. Reduce el riesgo de toparse con funcionalidades no muy maduras.
- Dependencies: Buscador de dependencias con los starters de Spring boot disponibles. Las
dependencias más habituales son:
- Spring Web se deberá escoger cuando se desee hacer una aplicación web o microservicios, siempre que se requiera una comunicación http y por tanto el uso de Spring MVC.
- Thymeleaf Incorpora el motor de plantillas para HTML dinámico, sucesor de los anteriores JSP (Java Server Page).
- Spring Data JPA necesario para utilizar la capa estándar de acceso a base de datos SQL denominada Java Persistence Api.
- Spring Security Permite incorporar controles de acceso en base a usuarios y roles sobre URLs de la aplicación. También habilita el control de ejecución de métodos de servicio en base a roles según los estándares J2EE.
- Lombok Aporta utilidades que facilitan la programación como la creación de @Getters y @Setters automáticamente para las clases que forman parte del conjunto de mensajes.
- Flyway Librería que permite aplicar scripts de cambios de base de datos de manera controlada cuando arranca la aplicación. Estos scripts se denominan migraciones y están sujetos a un control de versiones para garantizar que se aplican en el orden correcto.
- Mysql/Postgresql Incluye el JAR que contiene el driver JDBC necesario para configurar la capa de JPA según la base de datos a usar.
- Otras… El asistente permite seleccionar entre más de 50 dependencias e integraciones de herramientas open source dentro de los proyectos realizados con Spring.
Una vez seleccionados los parámetros que se quieren, haciendo click en el botón «Generate-Ctrl+» se descargará un archivo zip con el nombre del Artifact que contendrá la carpeta con la estructura de la aplicación lista para importar desde el IDE.
Recursos generados por SpringBoot Initializr
Para realizar la importación, bastará con importar o abrir el fichero pom.xml que viene dentro del comprimido. Los editores crearán y configurarán el classpath y las librerías que se indican en él para que estén disponibles para el programador.
El contenido del fichero tendrá la siguiente forma:
.
├── HELP.md
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
├── main
│ ├── java
│ │ └── org
│ │ └── eadp
│ │ └── spring
│ │ └── SpringAppApplication.java
│ └── resources
│ ├── application.properties
│ ├── static
│ └── templates
└── test
└── java
└── org
└── eadp
└── spring
└── SpringAppApplicationTests.java
De los ficheros incluidos, el más importante es MyWebAppApplication.java que corresponde con el punto de entrada a la ejecución del programa. Aquí es donde se aloja el método public static void main(String[] arg) que es el que inicializa toda la aplicación de Spring Boot.
Durante el arranque del framework, Spring revista el resto de directorios o paquetes que cuelgan de método main en búsqueda de clases marcadas con alguna anotación que permite el registro de componentes como: @Service, @Component, @Repository y demás anotaciones de Spring y de Spring MVC como @Controller o @RestController. Fíjese que el paquete utilizado es el com.eadp.web, esta opción puede especificarse en el asistente Spring Initializr.
El siguiente fichero más importante es application.properties que es donde se aloja toda la configuración de los componentes de Spring Boot, cómo que encoding usar (utf-8), si debe usar caché en las plantillas, qué nombre y clave de acceso son los de por defecto, etc… En el enlace de configuraciones y propiedades comunes de spring boot hay una referencia de todas las existentes y sus valores iniciales.
El siguiente fichero es MyWebAppApplicationTests.java donde se aloja el primer test de ejemplo que genera Initializr por nosotros. Ahí, el usuario debe añadir los test que considere oportunos para aplicar en la medida de lo posible desarrollo orientado a test o TDD.
Los otros directorios son static que es donde se alojan los recursos estáticos que la aplicación debe servir sin procesar. Por ejemplo en esta carpeta se incluyen los ficheros CSS, Javascript, imágenes o fuentes que se referencian desde el HTML.
Si el fichero alojado en este directorio está en una sub-carpeta de static, como puede ser static/css/main.css. Spring Boot lo publicará automáticamente en la url http://localhost:8080/css/main.css. Así que no debe haber información sensible que cuelgue del directorio static porque entonces será accesible abiertamente.
Y por último la carpeta templates almacena los ficheros que permiten generar HTML dinámicamente con algún motor de plantillas soportado por Spring como Thymeleaf o Freemarker.
Cómo añadir funcionalidad al proyecto
El proyecto creado es muy simple, si tratamos de arrancar el método main de la clase MyWebAppApplication, veremos cómo Spring inicializa toda la aplicación y se pone a la escucha del puerto 8080, pero si dirigimos el navegador a esa URL no aparecerá ningún contenido. Así que veamos cómo podemos hacer para imprimir algunos datos mediante html dinámico.
Para que las siguientes líneas sirvan debemos asegurarnos de haber escogido al menos las dependencias de Spring Web y Thymeleaf, dejando el resto de opciones como se indican en las líneas superiores. Generamos el proyecto de nuevo, de no ser así y crearemos el primer controlador que permitirá añadir código Java y una plantilla Thymeleaf que generará los primeros documentos HTML dinámicos.
Con el proyecto ya importado en el editor vamos a crear un primer controlador que será el encargado de ejecutar código Java dada una URL. En éste crearemos una variable que podrá ser impresa en la plantilla mediante el traspaso del dato vía el modelo.
// contenido del fichero: src/main/java/org/eadp/spring/controller/MyController.java
package org.eadp.spring;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import java.time.LocalDateTime;
@Controller
public class MyController {
@GetMapping("/")
public String index(Model model) {
model.addAttribute("tstamp", LocalDateTime.now());
return "index";
}
}
El controlador registra un método en «/», la raíz del sitio, de tal manera que cuando la aplicación esté arrancada y lista para escuchar peticiones http, normalmente por el puerto 8080, el método index será ejecutado cuando se solicite la URL de inicio http://localhost:8080/. En ese momento, el método será ejecutado añadiendo la hora actual a una variable con nombre tstamp que se guarda en model, objeto que se pasa a la vista, para que pueda construir el html dinámicamente. En este caso, mostrando la hora en la que se invocó ese método.
Por otro lado, nada más ejecutarse el método del controlador, Spring pasará el control a la vista con nombre index correspondiente al fichero index.html de la carpeta de templates, tal y como indica el return «index», en el que no hace falta indicar la extensión .html.
Para completar el círculo y ver los resultado necesitamos crear la vista index.html con el siguiente contenido:
// contenido del fichero: src/main/resources/templates/index.html
<!DOCTYPE html>
<html lang="es" xmlns:th="http://www.thymeleaf.org">
<head>
<title>My First Spring Boot App</title>
<style>
html {
font-family: "DejaVu Sans", Arial, Helvetica, sans-serif;
color: #404040;
background-color: #d3d3d3;
}
body {
padding: 5em;
}
</style>
</head>
<body>
<h1>Hello from Spring Boot!</h1>
<p>This page is served at <span th:text="__$_{__tstamp}"></span></p>
</body>
</html>
Y listos! Ahora ya podemos arrancar de nuevo la aplicación y hacer la petición a http://localhost:8080/. Dando como resultado la impresión de la hora en la que se ejecutó el controlador.
Securizando una aplicación Spring Boot
Spring Security es el módulo del proyecto Spring para incorporar seguridad de acceso a las aplicaciones hechas con Spring Boot. Permite controles de acceso por URL entre otras muchas opciones y es más que suficiente para proteger tu programa.
¿Qué es Spring Security?
Spring Security es una librería que forma parte del paraguas del proyecto Spring. Spring tiene más de 25 sub proyectos o módulos que aportan funcionalidad que las aplicaciones pueden utilizar si lo creen conveniente. En este caso Spring Security trata de agrupar todas las funcionalidades de control de acceso de usuarios sobre proyectos Spring.
El control de acceso permite limitar las opciones que pueden ejecutar un determinado conjunto de usuarios o roles sobre la aplicación. En esta dirección, Spring Security controla las invocaciones a la lógica de negocios o limita el acceso de peticiones HTTP a determinadas URLs.
Para ello, el programador debe realizar una configuración sobre la aplicación indicando a Spring Security cómo debe comportarse la capa de seguridad. Y aquí es una de las grandes ventajas de Spring Security ya que permite realizar toda una serie de parametrizaciones y ajustes para un gran abanico de posibilidades, permitiendo que el módulo se adapte bien a casi cualquier escenario de aplicaciones realizadas con Spring IoC o Spring Boot.
Si la implementación de Spring Security no fuera suficiente, aun así, proporciona toda una serie de interfaces Java que el programador puede implementar para cambiar el modo de comportarse de una determinada funcionalidad, como el ir a buscar los usuarios a una fuente de datos muy particulares.
Siempre que se pueda, conviene usar las implementaciones provenientes en la librería de Spring Security, ya que es código muy maduro ampliamente testado y además se mantiene actualizado por la comunidad cuando se publica un aviso de seguridad o ante cualquier publicación un nuevo estándar como pasó al liberarse el protocolo HTTP 2.
En definitiva, Spring Security es el método más conveniente para incorporar una capa de seguridad en donde se desea que sólo algunos usuarios tengan acceso a métodos y controladores de una aplicación Spring en base al uso de roles de usuario.
Además, al ser un proyecto maduro y ampliamente utilizado, es posible encontrar implementaciones incluidas de serie para los principales sistemas de seguridad de usuarios más desplegados en el mundo del desarrollo de software o en la administración de sistemas de TI, como LDAP o Kerberos entre otros.
¿Cómo incluir Spring Security en una aplicación web?
Añadir Spring Security en cualquier proyecto Spring de Java es muy sencillo, sobre todo si se ha utilizado la capa de auto-configuración de Spring MVC y Spring IoC denominada Spring Boot. Si deseas incorporar Spring Security en una aplicación Java o Spring MVC conviene que consideres la migración a Spring Boot antes de proceder con los siguientes pasos, ya que el uso de Spring Boot simplifica enormemente las acciones de configuración que debe realizar el desarrollador para poner en marcha cualquier aplicación Spring.
Suponiendo que tienes una aplicación Spring Boot en marcha gestionado por Maven, en donde el fichero pom.xml tiene como parent el artefacto spring-boot-starter-parent, lo primero que se debe hacer es incorporar el conjunto de dependencias provenientes de spring-boot-starter-security. Ésta traerá de forma transitiva el resto de librerías JARs que Spring necesitará para aplicar los mecanismos de seguridad requeridos.
Por sólo incluir la dependencia, si el programador no realiza ninguna configuración adicional, Spring Boot de manera predeterminada protegerá todo el acceso a la aplicación, impidiendo que ningún usuario no identificado pueda invocar a cualquier controlador. Este mecanismo tan restrictivo suele ser suficiente para pequeñas aplicaciones donde sólo se necesita restringir el acceso de forma general, pero queda algo reducido en aplicaciones donde hay secciones accesibles y otras protegidas, dependiendo de si el usuario está identificado o de si éste tiene un rol determinado.
Como el resto de aspectos configurables de Spring Boot, en el fichero de configuración de la aplicación, application.properties o application.yml, hay varias propiedades que pueden ajustarse para controlar el comportamiento base de Spring Security.
Las propiedades más importantes de Spring Security son:
spring.ldap.\* = ... # propiedades correspondientes a integración con LDAP
spring.security.oauth2.\* = ... # parámetros para OAuth2 y JWT
spring.session.\* = ... # configuraciones para la sesión HTTP, con persistencia SQL opcional
spring.security.user.name = user # usuario por defecto
spring.security.user.password = # password por defecto
spring.security.user.roles = # roles por defecto
De esta manera, sólo por incluir la dependencia, en el fichero de propiedades de la aplicación puede indicarse un usuario y clave por defecto que podrá usarse para tener acceso a los controladores, y como consecuencia a las diferentes pantallas HTML de la aplicación. Cuando el usuario intente acceder a cualquier URL de la aplicación, Spring Boot y el Security Filter de HTTP redirigirá al usuario al formulario de identificación, donde solicitará al usuario a insertar el nombre y password para proceder. En ese formulario se debe indicar los valores fijados en spring.security.user.name y spring.security.user.password. Si el usuario ha introducido los valores correctos, se considera al usuario autenticado, y sin tener en cuenta el rol, dejará continuar al usuario con una navegación normal.
Se debe tener en cuenta que la configuración por defecto no incluye el auto registro de usuarios. En el caso de necesitar que los usuarios puedan registrarse automáticamente, por ejemplo con un email, se deberá programar el mecanismo por el cual se confía en la información proporcionada por el usuario procediendo a la creación de éste en la tabla de datos o fuente de usuarios confiables usada por la aplicación.
¿Cómo proteger secciones de la aplicación de SpringBoot?
Si la protección de forma global a toda la aplicación no se adapta a tus necesidades, probablemente será porque hay secciones que deseas que sean accesibles mientras que otras estén protegidas para un determinado número de usuarios. Si este es el caso, al paso anterior de incluir la dependencia spring-boot-starter-security se debe acompañar con la creación de una clase de configuración de Spring anotada con @Configuration, en donde se podrá especificar qué secciones estarán habilitadas y cuáles no en función de los patrones de URL. Por comodidad, conviene que esta clase a crear extienda a WebSecurityConfigurerAdapter ya que simplifica la implementación.
Por ejemplo, supóngase que la aplicación web que se está desarrollando tiene una parte pública compuesta por páginas HTML, otra parte protegida y accesible para usuarios autenticados (sin importar su rol) y por último otra sección accesible únicamente para el o los usuarios con el rol de administrador. Para poder llevar a cabo esta configuración se debe segmentar los accesos por URLs de tal manera que permita realizar la siguiente distribución:
- /admin/* Patrón de URL que agrupa las funcionalidades del administrador
- /user/* Grupo de funcionalidades de los usuarios autenticados
- /* Resto de accesos públicos a la aplicación
Para que el mecanismo de control de acceso por URL funcione correctamente conviene hacer esta división por espacios de URL e incluir las secciones más restrictivas primero, dejando al final las más generales. Así se simplifica el número de configuraciones necesarias a realizar.
La configuración de Spring Security necesaria para este supuesto quedaría de la siguiente manera:
@Configuration
public class ArtecoCmsSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.regexMatchers("/admin/.\*").hasRole("ADMIN")
.regexMatchers("/user/.\*").authenticated()
.anyRequest()
.permitAll()
.and()
.formLogin();
}
}
Con esta simple configuración se ha separado el espacio de direcciones que pueden llegar a la web en base a tres conjuntos, los que contienen /admin que requerirá que el usuario esté identificado y además disponga del rol de ADMIN como mínimo. La segunda sección delimitada por direcciones que comiencen por /user que bastará que el usuario haya pasado por éxito a través del formulario de identificación. Y por último el resto de URLs quedarán abiertas para cualquier usuario identificado o no.
La última línea de configuración correspondiente a formLogin habilita el formulario de identificación de usuario, que se interpondrá en el usuario automáticamente si trata de acceder a alguna URL que requiera autenticación o algún ROL.
Cómo administrar los usuarios de Spring Security
Tener un sólo usuario en la aplicación puede ser útil en ciertas ocasiones, aunque desde luego es un escenario algo limitado para aplicaciones abiertas en Internet. Por norma general se usarán varios usuarios, presumiblemente con roles de acceso diferentes para limitar las acciones que pueden realizar cada tipo de usuario.
Para ello necesitamos añadir una fuente de usuarios administrador por la aplicación y que serán usados en el proceso de autenticación por Spring Security. De esta manera, nuestra aplicación puede incorporar un mecanismo de registro de usuarios que puedan autenticarse.
Para que Spring pueda obtener la información de un usuario durante el proceso de login, se solicitará al conjunto de clases Java registradas en el contexto de Spring, de alguna que implemente la interfaz UserDetailsService:
package org.springframework.security.core.userdetails;
public interface UserDetailsService {
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}
Esta interfaz tiene un único método de obligada implementación que es aquel que devuelve los detalles de un usuario UserDetails dado un nombre de usuario, el que trata de ingresar en la aplicación. Por tanto será obligatorio implementar un bean con esa interfaz dando cuerpo a ese método, por ejemplo como el siguiente:
@Component
public class MyUserDetails implements UserDetailsService {
private final UserRepository userRepository;
@Autowired
public ExtranetUserDetails(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Optional<MyUser> userDb = userRepository.findByUsername(username);
if (userDb.isPresent()) {
MyUser user = userDb.get();
List<Permission> permissions = user.getPermissions();
Set<String> roles = new HashSet<>();
if (!CollectionUtils.isEmpty(permissions)) {
for (Permission p : permissions) {
roles.add(p.getRol().name());
}
}
return org.springframework.security.core.userdetails.User
.withUsername(username)
.roles(roles.toArray(new String\[0\]))
.password(user.getPassHash())
.build();
} else {
throw new UsernameNotFoundException("Usuario no encontrado!");
}
}
}
La implementación de este bean dependerá de dónde tenemos registrados los usuarios. Normalmente éstos estarán en una tabla de la base de datos, así que ya sea con SQL nativo o usando JPA con EntityManager o a través de Spring Data Repositories deberemos consultar la fila asociada según el nombre de usuario. Una vez localizado sólo restará convertir o mapear ese objeto a uno esperado por Spring, del tipo UserDetails.
Por último, en la clase de configuración de Spring Security registraremos nuestra clase recién creada:
@Configuration
public class ArtecoCmsSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// ... código omitido
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myUserDetails()).passwordEncoder(new BCryptPasswordEncoder());
}
@Autowired
public MyUserDetails myUserDetails(UserRepository userRepository) throws Exception {
return new MyUserDetails(userRepository);
}
}
¡Y listos! Con esta simple configuración ya se tiene una aplicación con gestión de usuarios capaces de identificarse en Spring Security y por tanto usar todos los mecanismos que esta capa de seguridad ofrece.
Se debe tener en cuenta que en el ejemplo se ha optado por usar un PasswordEncoder que permite almacenar las contraseñas de los usuarios de forma encriptada en la base de datos. Esta manera es una gran aliado para evitar fugas de información importantes si algún usuario malintencionado pudiera tener en algún momento acceso a la tabla de usuarios, evitando que se haga con las credenciales de los usuarios.
Cuando tenga que implementarlo en su aplicación no debe olvidar proporcionar una implementación válida como UserRepository para que pueda obtenerse el usuario solicitado. Recuerde que este información puede proceder de una base de datos o también de algún archivo, o incluso de algún servicio remoto para la centralización de usuarios como LDAP.
Desplegar una App Spring Boot con Docker
Desplegar un programa con forma de Html dinámico realizado con Spring Boot y Java no puede ser más fácil con Docker. A continuación explicamos los pasos a seguir. Obviamente para poder desplegar una aplicación de Java que incluye el framework Spring Boot necesitarás tener las herramientas de Java instaladas de forma local. Aunque puede hacerse también con Gradle, recomendamos que uses Maven para empaquetar la web. Y por último necesitarás tener instalado Docker.
Preparar la aplicación de Spring Boot
El primer paso es asegurarnos de que la aplicación funciona correctamente al estar empaquetada en un único archivo jar. Esto es fácilmente lograble si hemos usado Maven como herramienta de construcción de proyectos Java
asegurándonos de que hemos incluido el plugin siguiente en el fichero pom.xml
<project attr="...">
<!-- .... -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Indicar la versión del plugin es opcional si nuestro proyecto tiene como parent
el proyecto
paraguas de Spring Boot denominado spring-boot-starter-parent
. Si no lo tienes puede incluir el
siguiente fragmento, normalmente al principio del fichero pom.xml, después de project:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.4.RELEASE</version>
<relativePath/>
</parent>
Habiendo realizado la configuración citada, el siguiente paso es invocar a Maven para verificar que el bundle es correcto y contiene tanto nuestra aplicación compilada como las dependencias jars contenidas dentro.
mvn clean package
java -jar target/app.jar
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _\` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\\__, | / / / /
=========|\_|==============|___/=_/\_/\_/
:: Spring Boot :: (v2.2.4.RELEASE)
La aplicación debe arrancar correctamente, sin mostrar ningún aviso de error quedándose a la escucha en el puerto Http indicado en la configuración, normalmente el 8080. De tal manera que al abrir el navegador en la url http://localhost:8080 se debe mostrar contenido de nuestro proyecto.
Llegados a este punto podemos asegurar que la aplicación funcionará desde dentro de un contenedor gestionado por Docker.
Construir la imagen de Spring Boot para Docker
Una vez comprobado que la aplicación en un fat-jar obtenido a través del proceso de empaquetado de Maven, el siguiente paso es crear una imagen de la aplicación para que pueda ser usada en un
contenedor virtualizado. Para ello se debe crear el fichero Dockerfile
con los pasos necesarios
para realizar el aprovisionamiento de la imagen. Afortunadamente, como usamos un fat-jar que
contiene todas las dependencias requeridas dentro de él el aprovisionamiento se reduce a simplemente instalar la máquina virtual de Java.
De nuevo, el proceso es muy simple ya que partiremos de una imagen base que ya contiene el JDK que necesitamos. Se mantienen actualizadas las imágenes base con todas las versiones activas del JDK.
Así, el fichero Dockerfile
queda tan simple como:
FROM openjdk:8-alpine
ADD target/my-fat.jar /usr/share/app.jar
ENTRYPOINT ["/usr/bin/java", "-jar", "/usr/share/app.jar"]
La primera línea indica que queremos usar la versión JDK-8 sobre un sistema mínimo como Alpine Linux para asegurarnos de que el tamaño del archivo generado es lo más pequeño posible. La segunda línea añade nuestra aplicación al empaquetado resultante, y por último se indica el comando de arranque que debe utilizar el contenedor una vez inicializado.
Habiendo escrito el fichero de aprovisionamiento es hora de realizar la construcción de la imagen:
docker build -t my_docker_hub_username/my_image_name:my_image_version .
Importante el punto (.) al final, este indica el directorio actual, en donde está el fichero Dockerfile. El argumento -t user/image:tag
permite nombrar la imagen. Esta puede ser distribuida
públicamente mediante Docker Hub, o de forma privada a través de algún
Registry como Harbor privado como los disponibles en Amazon Web Services o Google Compute Engine.
El comando de construcción generará una salida similar a la siguiente:
Sending build context to Docker daemon 86.11MB
Step 1/4 : FROM openjdk:8-alpine
8-alpine: Pulling from library/openjdk
e7c96db7181b: Pull complete
f910a506b6cb: Pull complete
c2274a1a0e27: Pull complete
Digest: sha256:94792824df2df33402f201713f932b58cb9de94a0cd524164a0f2283343547b3
Status: Downloaded newer image for openjdk:8-alpine
---> a3562aa0b991
fetch https://dl-cdn.alpinelinux.org/alpine/v3.9/main/x86_64/APKINDEX.tar.gz
fetch https://dl-cdn.alpinelinux.org/alpine/v3.9/community/x86_64/APKINDEX.tar.gz
Executing busybox-1.29.3-r10.trigger
OK: 123 MiB in 62 packages
Removing intermediate container 8c14d7443332
---> 0c3f73a47bff
Step 3/4 : ENTRYPOINT \["/usr/bin/java", "-jar", "/usr/share/app.jar"\]
---> Running in 0392060d1087
Removing intermediate container 0392060d1087
---> 23655b67c59f
Step 4/4 : ADD target/my-fat.jar /usr/share/app.jar
---> df132592b5f8
Successfully built df132592b5f8
La nueva imagen está disponible localmente y puede confirmarse con docker images
Arrancar contenedor con Spring Boot
Ya tenemos todo lo necesario para arrancar nuestro contenedor que ejecute la imagen construida en el paso anterior. Es importante saber que con cada nueva versión de la aplicación deberemos construir de nuevo la imagen, ya que por norma general los contenedores deben ser autosuficientes. Todo lo que necesiten deberá constar o estar aprovisionado en la imagen para asegurar que el entorno es estable y reproducible aun usando los contenedores, entidades altamente volátiles.
Aunque las imágenes son estáticas, en el momento de arrancar un contenedor podemos hacerle llegar parámetros en forma de variables de entorno o propiedades del sistema Java, usando
-Dargument=value
. Muy útil para indicar el profile
de Spring que queremos que se active en el
contenedor, por ejemplo para que use una u otra conexión con base de datos.
Aunque hay muchas formas, en nuestro caso usaremos una variable de entorno para indicar el perfil
activo en la aplicación. Esto debe hacerse indicando el nombre de perfil a usar en la variable de
entorno SPRING_PROFILES_ACTIVE
tal y como indica la documentación oficial de Spring Boot.
También se debe indicar qué puerto del contenedor debe estar abierto, para que las peticiones http lleguen al proyecto. Por tanto, se debe añadir un argumento que configure que el tráfico que llegue en un puerto local, se redirija al puerto 8080 (por defecto de Spring) del contenedor.
Dicho esto, ya sólo queda lanzar un contenedor usando la imagen anterior e indicando el perfil a activar:
docker run -p 8080:8080 --env SPRING_PROFILES_ACTIVE=docker \
my_docker_hub_username/my_image_name:my_image_version
Y listos! Esta sentencia deja ejecutando un contenedor con nuestro nuevo proyecto del millón de dólares ejecutándose en local. Esta misma configuración podrá servirte para desplegar la aplicaciónen un entorno abierto en Internet, utilizando la plataforma de orquestación de contenedores Kubernetes de Google, Amazon o Microsoft.
Conclusiones
Spring Boot ha revolucionado el desarrollo en Java, convirtiéndose en el framework más popular del mundo. Su flexibilidad, eficiencia y amplia gama de herramientas lo hacen ideal para cualquier proyecto web. ¿Quieres aprovechar al máximo esta potente herramienta? Visita la web de Arteco Consulting SL y descubre cómo podemos ayudarte a dominar Spring Boot y llevar tu desarrollo al siguiente nivel. ¡No te quedes atrás, únete a la revolución de Spring Boot con Arteco Consulting, SL!
ÍNDICE
RELACIONADOS
CATEGORÍAS
java
spring boot
tutorial
Mantente Conectado
Newsletter
¡Mantente al día con lo último en tecnología y negocios! Suscríbete a nuestra newsletter y recibe actualizaciones exclusivas directamente en tu correo.
Reunión Online
No dejes pasar la oportunidad de explorar nuevas posibilidades. ¡Agenda una reunión online con nosotros hoy y comencemos a construir juntos el futuro de tu negocio!
- :D
- :)
- ;]
- :0
- :}
- ;)
- :>
Únete al Equipo
Contamos con una gran cartera de noveles que compaginan su formación académica con la experiencia en Arteco, aprendiendo de la mano de los que están en primera línea. Realizamos un programa intensivo de formación cara a la rápida incorporación en equipos de desarrollo reales.
- :)
- :0
- :D
- ;)
- :]
- :º