En el ojo de la tormenta

Viviendo en la era digital no hay duda de que la información es un recurso muy valioso, y como mencionaba mi compañera María Ángeles en su entrada, el que tiene la información tiene el poder, pero ser capaz de sintetizarla y inferir nuevas evidencias o predicciones a partir de esta en un tiempo razonable es también muy importante.

Cada día se almacenan enormes cantidades de información, por lo que sistemas gestores de bases de datos relacionales están dando paso al fenómeno big data, que cada vez despierta más interés y expectativas en las organizaciones. De hecho, muchas empresas están abriendo nuevas líneas de negocio, creando plataformas SaaS que se dedican exclusivamente a analizar cantidades titánicas de información e identificar aquella que podría tener un valor importante para las decisiones estratégicas de muchas corporaciones.

Para aquellos que todavía no están familiarizados con el término, se trata de la nueva generación de tecnologías para el acceso, procesamiento, análisis y visualización de enormes volúmenes de datos. Entre estas surge toda una tipología de bases de datos NoSQL y plataformas de procesamiento big data, sistemas robustos que ofrecen elevadas capacidades de escalabilidad, baja latencia, y un esquema libre de modelo para el almacenamiento flexible de datos. Así, tenemos familias para el almacenamiento de datos en forma de clave-valor (key-value), orientados a colecciones, columnas, persistencia de estructuras de datos en forma de grafos, etc. Uno de ellos es Storm (el lector precavido ya puede estar intuyendo de donde proviene la inspiración para el título de esta entrada).

Storm es el sistema distribuido para el procesamiento de flujo de datos, actualmente propiedad de Twitter, aunque originalmente fue desarrollado por la compañía Blacktype. Fiable y tolerante a fallos, garantiza que cada mensaje inyectado en el flujo se procesará de forma completa. A diferencia de otras plataformas de procesamiento de datos como Hadoop o Cascading, Storm realiza el procesamiento del flujo de datos en tiempo real. El componente encargado de llevar a cabo ese procesamiento se llama topología. A su vez, ésta está formada por varias abstracciones que explicaremos más adelante. Cuando la carga computacional se incrementa de forma considerable se hace trivial aumentar el paralelismo de la topología para que se ejecute en varios procesos distribuidos en multitud de máquinas físicas. Si se produce un fallo durante el procesamiento, Storm se encargará de reasignar el trabajo a las tareas, además de garantizar que no habrá pérdida de ningún mensaje gracias a la naturaleza transaccional de las topologías. Otra de las ventajas de Storm, es que permite la ejecución de consultas distribuidas (DRPC) paralelizables al vuelo, ideal para los escenarios donde es necesario lanzar queries intensas u otras operaciones exigentes.

Es usado por multitud de organizaciones tales como Groupon, Alibaba, The Weather Channel, y como no, Twitter. Podemos ver la lista completa de empresas y proyectos que usan Storm. No tenemos que olvidarnos de mencionar que se trata de una plataforma open source, y aunque está desarrollada en una combinación de Clojure y Java, Storm es completamente agnóstico de lenguaje, por lo que nada nos limita a crear las topologías en otros lenguajes de programación.

El cluster de Storm está compuesto por dos tipos de nodos:

  • Nodo maestro, es el nodo principal donde se ejecuta el demonio llamado Nimbus, encargado de asignar tareas a los nodos worker, monitorizar posibles fallos, distribuir el código a lo largo del cluster, etc.
  • Nodos worker alojan el demonio llamado Supervisor que ejecuta la porción de la topología entre varios procesos que incluso puedan estar ejecutándose en múltiples máquinas.

La coordinación y el estado de ejecución entre los nodos se mantiene en ZooKeeper, o bien se persiste en disco duro cuando se ejecuta en modo local, y es precisamente esto lo que hace que Storm sea tan robusto y estable.

Las topologías se ejecutan continuamente hasta que se solicite explícitamente su terminación. Éstas están formadas por dos tipos de componentes que se agrupan en un grafo. Unos llamados spouts representan la fuente de entrada de flujo de datos en el sistema y generan listas de tuplas. Los spouts pueden inyectar datos en la topología bien consumiendo mensajes de las colas de los sistemas de mensajería, consultando la API de terceros vía REST, leyendo registros desde la base de datos, o cualquier otra fuente que se nos ocurra. Los bolts consumen las tuplas emitidas por spouts u otros bolts, para aplicar cualquier tipo de transformación o lógica específica al flujo de datos, ya sea filtrado, agrupaciones, funciones, agregaciones, etc. En el siguiente diagrama podemos ver la estructura simplificada del cluster de Storm.

Para dar un enfoque pragmático e ilustrar las capacidades de Storm, vamos a desarrollar una sencilla aplicación para procesar datos obtenidos desde las redes sociales. Usaremos el API de Facebook, para recuperar los estados que han ido publicando nuestros amigos y los mandaremos a una cola RabbitMQ. A continuación desarrollaremos los componentes de la topología – uno se encargará de consumir los mensajes de la cola y generar tuplas con el estado y el publicador (spout), y el resto aplicarán las transformaciones correspondientes para separar cada estado en palabras, contar las ocurrencias de las mismas y mostrar el top 20 de palabras más utilizadas en los estados de nuestros amigos en tiempo real. En la imagen podemos ver el grafo de spouts y bolts que forman nuestra topología.

El código completo de la aplicación se encuentra en el repositorio github, por lo que no vamos a entrar en detalles de implementación. Tan solo mencionaremos que los spouts deben implementar la interfaz IRichSpout y los bolts la interfaz IRichBolt. Los métodos importantes de los spouts, son nextTuple donde se obtiene el flujo de datos y se generan las tuplas, y el par ack / fail para confirmar o bien señalizar el fallo en el procesamiento de la tupla. Esto es importante, ya que la emisión de una tupla por un spout puede desencadenar la generación de muchas tuplas en otros componentes, por lo tanto se considera que un mensaje se ha completado satisfactoriamente cuando se procesa correctamente el árbol de tuplas generadas.

A la hora de ensamblar la topología podemos especificar las preferencias de paralelismo, tanto el número de tareas como el número de procesos workers. El último argumento del constructor de los componentes especifica el paralelismo. TopologyBuilder nos permite construir la topología que más tarde se desplegará en el cluster:

TopologyBuilder topologyBuilder = new TopologyBuilder();
topologyBuilder.setSpout("status-emitter", new AmqpStatusEmitterSpout() );
topologyBuilder.setBolt("status-splitter", new StatusSplittingBolt())
        .shuffleGrouping("status-emitter");
topologyBuilder.setBolt("status-segmentator", new StatusSegmentatorBolt(), 
        5).fieldsGrouping("status-splitter", new Fields("word"));

StatusSegmentatorBolt se ejecutará de forma paralela entre 5 hilos de ejecución.

A raíz de todo lo que hemos dicho, podemos concluir que Storm es una plataforma prometedora y gracias a su modelo de programación sencillo, la curva de aprendizaje del mismo es corta y no tendremos que preocuparnos por detalles de sincronización, paralelismo o mantenimiento del estado y coherencia en el procesamiento de los mensajes.

Arquitecturas robustas y seguras con CQRS (I)

La explotación de los fallos de seguridad en las arquitecturas de software no siempre viene determinada por las vulnerabilidades descubiertas en las tecnologías usadas para construir el sistema, sino también por el deficiente diseño por parte de analistas y/o malas prácticas y anti patrones usados por los propios desarrolladores. Es común encontrar programadores que todavía usan las tecnologías obsoletas, se sienten cómodos incrustando código Java dentro de las páginas JSP, o están usando operadores del propio lenguaje para concatenar los parámetros de las consultas / sentencias SQL y un largo etcétera de disparates. Incluso en las arquitecturas en capas, la ausencia de patrones de diseño tiene como resultado un sistema altamente acoplado y difícilmente escalable. Además, la capa de negocio empieza a delegar las responsabilidades impropias, como el manejo de transacciones, auditoría, seguridad – concerns transversales que se deberían encapsular y aplicar de forma transparente.

Las consecuencias de todo esto a largo plazo pueden llegar a ser realmente caóticas. El mantenimiento y la implementación de nuevos requisitos funcionales cada vez se vuelve más complicado, haciendo que el número de fallos y vulnerabilidades para comprometer el sistema se multiplique. Hacer software de gran calidad y aplicar la integración continua, desde luego no es tarea trivial, aun menos, hoy en día, cuando los usuarios cada vez son más exigentes y piden aplicaciones más potentes, rápidas y flexibles.

¿Quizás haya llegado el momento de cambiar el enfoque tradicional de desarrollo de software? Eso es lo que efectivamente persigue CQRS. Detrás de estas siglas (Command Query Responsability Segregation) se esconde un patrón arquitectónico con un fundamento inicial muy sencillo: separar la aplicación en dos partes – una que se responsabiliza de ejecutar acciones y otra encargada de lanzar las consultas. Aunque a primera vista puede parecer algo irrelevante, este enfoque nos abre una plétora de posibilidades. Para comprender mejor el paradigma CQRS, vamos a explicar cada uno de los componentes que lo forman ayudándonos del siguiente gráfico.

  • Comandos – son objetos que encapsulan tanto la intención del usuario como la información necesaria para llevar a cabo la operación. Por ejemplo: CreateNewUserCommand. Los comandos se envían al bus de comandos que se encargará de despachar cada comando a su correspondiente manejador. El cambio de estado en el sistema se inicia mediante la ejecución del comando.
  • Modelo de dominio – representa el corazón del sistema. Debido a que CQRS está basado en su predecesor DDD (Domain Driven Design) el enfoque principal de diseño reside sobre el modelo de domino no anémico. ¿Qué quiere decir esto? En el diseño tradicional, los objetos de dominio suelen desempeñar el rol de entidades que tan solo guardan el estado del sistema y carecen de cualquier tipo de comportamiento. Esto además suele crear ambigüedad entre los DTO (Data Transfer Objects) y los objetos del modelo, haciendo que estos últimos acaben teniendo información que se debe renderizar en la vista, y por lo tanto crear acoplamiento. Otra capa de servicios se dedica a alterar el estado del sistema. CQRS promueve un diseño enfocado completamente al modelo de dominio estrictamente de comportamiento.
  • Repositorios – proporcionan el acceso a los objetos de dominio y permiten aislar el modelo de dominio del mecanismo de persistencia. Los repositorios tan solo tienen que ser capaces de recuperar el agregado (objeto de dominio) a partir de su identificador único, mientras cualquier otro tipo de consulta se realizará sobre la base de datos de consultas.
  • Eventos – son la fuente de cualquier cambio de estado en la aplicación. Como hemos mencionado anteriormente, la ejecución de los comandos sobre el agregado, inicia el cambio de estado en el sistema, lo que a su vez producirá una serie de eventos de dominio. Aun más, ni siquiera deberemos persistir los objetos del dominio sino los eventos que se generan. Con esto tenemos la posibilidad de reconstruir el objeto de dominio a su último estado, tan solo aplicando el flujo de eventos sobre él. Este patrón se conoce como eventsourcing. Los eventos se mandan al bus de eventos, y se despachan a cualquier componente interesado.
  • Almacén eventos – se encarga de dar soporte a los eventos de dominio, es decir, gestionar su almacenamiento.
  • Consultas – una sencilla capa de servicios se encarga de recuperar los datos y rellenar la vista. La información que se desea renderizar en la vista se reflejará en el objeto DTO que contendrá los resultados de la consulta.

Con los conceptos un poco más claros, veamos que ventajas nos ofrece CQRS:

1. Permite distribuir la aplicación a través de múltiples máquinas físicas (alta escalabilidad).

2. Alta disponibilidad a nivel de aplicación – si se cae un componente el resto del sistema puede seguir funcionando.

3. Auditoría / historial / traza de las acciones de usuarios “out of the box“. Este tipo de auditoría no es comparable a ninguna infraestructura de log – los eventos aportan valor al negocio. Es fácil nutrir a plataformas de auto aprendizaje o motores de correlación a partir de estos eventos, para por ejemplo predecir las acciones del usuario, detectar anomalías, etc. Al tener la traza de todo lo que ocurre en la aplicación, tenemos una fuente de verdad única. La reproducción de fallos se hace más sencilla.

4. En vez de enlazar los objetos de dominio con los componentes de la UI, tenemos simples DTO que reflejan fielmente aquello que se quiere representar en la vista y se pueden recuperar directamente desde la base de datos. Así, podemos obtener toda la información necesaria en una única petición a la fuente de datos.

5. CQRS ayuda a escribir un modelo de dominio expresivo.

6. Cuando se necesita que un objeto de dominio procese un comando, el repositorio obtendrá el flujo de eventos relacionados con ese objeto del almacén de eventos. Entonces, se instanciará un nuevo objeto y se aplicarán todos los eventos sobre él. De esta forma, obtendremos el objeto en su estado original y no hará falta proporcionar persistencia para el modelo de dominio.

7. Modelos de datos separados. Éstos se mantienen consistentes, sincronizados y desacoplados gracias a los eventos que además proporcionan mecanismo para actualizar el modelo de lectura. Para el modelo de lectura podemos usar cualquier tecnología, desde JDBC, pasando por sistemas ORM, hasta soluciones NoSQL, ya que la única finalidad es rellenar la vista con datos lo más rápido posible. Podemos tener bases de datos denormalizadas para optimizar las lecturas e evitar realizar complejas consultas con muchas uniones.

Como ninguna tecnología es perfecta y válida para todas las soluciones, lo mismo pasa con CQRS. Estos son los puntos que se deben tener en cuenta si decidimos aplicar CQRS en nuestras aplicaciones:

1. Cuando el modelo de dominio es complejo. Un modelo sencillo no se beneficiará realmente de este patrón.

2. La curva de aprendizaje es relativamente alta y requiere cambiar la perspectiva sobre el diseño tradicional.

3. Mayores requerimientos de la infraestructura al tener dos modelos (lectura y escritura).

En el resto de casos, los beneficios de CQRS son enormes y las aplicaciones serán mucho más mantenibles, fácilmente extensibles y preparadas para los nuevos retos que cada día nos plantean las tecnologías / usuarios. La próxima vez veremos como aplicar CQRS en una aplicación sencilla usando Axon Framework. Con esto, asentaremos mejor todos los puntos teóricos vistos anteriormente.

Spring Security – Mejorando la infraestructura de seguridad

Después de un considerable parón volvemos con un post cargado de temas interesantes sobre nuestro framework de seguridad favorito. En la última entrada aprendimos los conceptos básicos de Spring Security – protección de las URLs, autenticación básica, securización de métodos de la capa de negocio, etc. Pero para desplegar la aplicación en un entorno de producción, todavía nos quedan una serie de puntos importantes por tratar.

Uno de los más cruciales es el almacenamiento seguro de las credenciales de usuarios. Actualmente el proceso de autenticación utiliza el gestor que almacena los datos en memoria (org.springframework.security.core.userdetails.memory.InMemoryDaoImpl) para validar las credenciales proporcionadas por el usuario. Ya vimos en su momento que era bastante trivial de configurar, pero al mismo tiempo sus desventajas nos impiden utilizarlo en escenarios más allá de una simple prueba de concepto. Debido a que la información se aloja dentro de la JVM, en cuanto se reinicie el servidor de aplicaciones se perderán todos los cambios que los usuarios podrían haber efectuado sobre sus cuentas. Al definir los usuarios y los roles como metadatos de la propia configuración de contexto de Spring Security, las contraseñas también se definen en texto plano.

Por tanto, necesitamos un mecanismo mucho más robusto y seguro para el almacenamiento de las credenciales, pero antes de comenzar con las explicaciones, vamos a ver las nuevas funcionalidades que se han incorporado en esta versión de la aplicación:

  • Página de login personalizada: Hemos proporcionado página de login propia para mantener el look & feel de nuestra aplicación.

  • Enlace de logout: Permite cerrar la sesión y redirigir al usuario a la página de login (zona no segura). Más tarde explicaremos detalladamente como se lleva a cabo este proceso y los filtros involucrados.
  • Creación de artistas: Ahora podemos dar de alta artistas. Las discográficas se recuperan mediante peticiones al método del controlador Spring MVC que genera el resultado en formato JSON, serializando directamente los objetos de dominio.

  • Adición de miembros: Por último, después de crear al artista el usuario es redireccionado a la vista mostrada en la imagen. Aquí se añaden los componentes (miembros) de cada grupo. Para cada miembro podemos elegir la imagen que se guardará en la base de datos como objeto binario (BLOB). En la parte derecha se muestran los miembros ya añadidos. Las imágenes también se renderizan desde la base de datos invocando peticiones al método del controlador. Éste se encarga de obtener el flujo binario de cada imagen y escribirlo en la salida del servlet. Para ver los detalles de implementación pueden examinar el código de la aplicación Artgasmator.

Empezamos viendo como sustituir la página de login por una propia. Si examinamos el código HTML de la página actual podemos observar el siguiente fragmento generado:

<form name='f' action='j_spring_security_check' method='POST'>
   <table>
      <tr><td>User:</td><td><input type='text' name='j_username' value=''></td></tr>
      <tr><td>Password:</td><td><input type='password' name='j_password'/></td></tr>
      <tr><td colspan='2'><input name="submit" type="submit"/></td></tr>
      <tr><td colspan='2'><input name="reset" type="reset"/></td></tr>
   </table>
</form>

Este formulario se encarga de mandar los parámetros j_username y j_password cuyos nombres están estandarizados por la especificación Java EE Servlet, y representan el nombre de usuario y la contraseña respectivamente. La acción es una URL que ni siquiera hemos definido o creado el mapeo de petición en alguno de los métodos del controlador. Se trata de una URL virtual de Spring Security, monitorizada e interceptada por el filtro UsernamePasswordAuthenticationFilter, cuyo propósito es extraer las credenciales de usuario de la petición HTTP. Para mayor claridad se proporciona el diagrama que ilustra el proceso completo.

Por lo tanto, para proporcionar una página de login personalizada, creamos un nuevo artefacto JSP y copiamos el código HTML anterior. Ahora solo falta implementar el controlador y decirle a Spring Security que use nuestra página de login:

@RequestMapping(value="/login", method=RequestMethod.GET)
public String login() {
	return "login";
}

Siempre que se detecte el patrón de URL “/login”, se devolverá la vista login que habíamos creado en el punto anterior. Dentro del elemento <security:http>, debemos declarar el <security:form-login> e inicializar su atributo login-page con la URL de la página (servida por el método login del controlador):

<security:http auto-config="true" use-expressions="true">
    <security:intercept-url pattern="/genres/create" access="hasRole('ROLE_ADMIN')"/>
    <security:intercept-url pattern="/*" access="hasAnyRole('ROLE_ADMIN','ROLE_USER')"/>
    <security:form-login login-page="/login"/> 
</security:http>

Si ahora intentamos iniciar sesión, en vez de ver tan esperada página de login, el navegador visualizará el siguiente mensaje:

Si se acuerdan de las reglas de autorización, solamente los usuarios con el rol USER o ADMIN tenían acceso a la aplicación. En el caso de usuario anónimo (cuando accedemos a la página de inicio de sesión), el gestor de decisión restringe el acceso al recurso “login”, y vuelve a redirigir el usuario a la página de login creando un bucle recursivo de redireccionamientos. Para solucionar esto, debemos declarar otra regla como primer elemento de <security:http> de manera que se otorgue acceso del recurso “login” a cualquier usuario:

<security:intercept-url pattern="/login" access="permitAll"/>

Ahora podemos acceder sin problemas a la página de login e iniciar sesión.

El funcionamiento de logout (cierre de sesión) también se basa en la intercepción de la URL virtual por parte del filtro LogoutFilter. Por defecto la URL que espera este filtro es /j_spring_security_logout. Sin embargo, Spring Security nos ofrece la flexibilidad de configurar la URL anteriormente dicha, así como otros parámetros adicionales a través del elemento <security:logout>. Veamos la configuración:

<security:http auto-config="true" use-expressions="true">
   <security:logout invalidate-session="true" logout-url="/logout"/> 
</security:http>

El atributo logout-url especifica la URL. También es posible definir la URL de redirección una vez llevado a cabo con éxito el proceso de logout, aunque en este caso lo vamos a dejar con su valor por defecto (la página de login). Por último, el atributo invalidate-session invalida la sesión del usuario actual, limpia las cookies, tokens de remember me, etc. Sólo nos falta crear el siguiente enlace:

<a class="logout" href="${ctx}/logout">Cerrar sesión</a>

La variable ctx contiene el contexto de despliegue de la aplicación web.

Con los cambios anteriores, nuestra aplicación ha experimentado una considerable mejora desde el punto de vista visual, aunque el mecanismo de autenticación de usuarios todavía usa el modelo de almacenamiento en memoria. El siguiente objetivo es habilitar la autenticación usando el DaoAuthenticationProvider que obtiene la información de usuarios desde la base de datos relacional. Para ello, Spring Security necesita una serie de tablas.

La tabla users almacena el nombre de usuario, la contraseña y un flag que indica si el usuario está habilitado. El mapeo de usuario con el rol / roles se guarda en la tabla authorithies. Por lo tanto, lo primero que tenemos que hacer es crear dichas tablas en nuestra base de datos ejecutando directamente el script SQL que se proporciona (véase el fichero de la aplicación al final de la entrada). Para activar la autenticación contra la base de datos tenemos que hacer el siguiente cambio en la configuración de Spring Security:

<security:authentication-manager alias="authenticationManager”>
   <security:authentication-provider>
      <security:jdbc-user-service data-source-ref="dataSource"/>
   </security:authentication-provider>
</security:authentication-manager>

Gracias a la flexibilidad del esquema security, no tenemos que preocuparnos por las definiciones de beans de la infraestructura de Spring Security. Como se aprecia, el elemento <security:jdbc-user-service> dispone del atributo data-source-ref cuyo valor será la referencia de la fuente de datos para establecer la conexión con nuestro servidor de bases de datos. Solo mencionar que Spring Security va más allá y nos permite definir grupos de autorización, adaptarnos al nuestro esquema existente de tablas de usuarios, roles y grupos proporcionando consultas SQL personalizadas, pero esos son temas que no vamos a tratar de momento.

Si quisiéramos dar de alta un nuevo usuario, podríamos lanzar directamente sentencias SQL contra la fuente de datos usando los templates de Spring para abstraer todas las complejidades e eliminar el código desbocado y redundante. La buena noticia es que Spring Security dispone de la implementación out of the box para dar de alta, actualizar, eliminar usuarios, cambiar contraseñas, etc. Concretamente se trata del servicio JdbcUserDetailsManager cuya declaración deberá ser la siguiente:

<bean id="userDetailsManager" 
            class="org.springframework.security.provisioning.JdbcUserDetailsManager">
   <property name="dataSource" ref="dataSource"></property>
   <property name="authenticationManager" ref="authenticationManager"></property>
</bean>  

A raíz de esto, también tenemos que hacer un pequeño cambio en la asignación del proveedor de autenticación para que referencie el servicio anterior:

<security:authentication-manager alias="authenticationManager">
   <security:authentication-provider user-service-ref="userDetailsManager">
   </security:authentication-provider>
</security:authentication-manager>

En el controlador UserController hemos implementando la lógica de creación de un nuevo usuario conectando automáticamente la referencia del bean userDetailsManager mediante la anotación Autowired:

@Autowired
private UserDetailsManager userDetailsManager;
	
@RequestMapping(value="/login", method=RequestMethod.POST)
public String createUser(@RequestParam("user")String username, 
				@RequestParam("password") String password,
				Model model) {
		
   List<GrantedAuthority> authorites = new ArrayList<GrantedAuthority>();
   authorites.add(new GrantedAuthorityImpl("ROLE_USER"));
		
   User user = new User(username, password, true, false, false, false, authorites);
		
   userDetailsManager.createUser(user);
		
   return "login";
}

El método createUser recibe como parámetro el objeto User, donde se especifica el nombre de usuario, contraseña y la lista de roles. Para el resto de operaciones (eliminación, actualización) las implementaciones serían análogas.

Aunque hayamos proporcionado persistencia de las credenciales, si examinamos la base de datos podremos ver que las contraseñas todavía se almacenan en texto plano, algo intolerable para una aplicación corriendo en entorno de producción. Por suerte Spring Security trae un abanico de cifradores de contraseñas listos para usar. Podemos escoger entre algoritmos md4, md5, sha, sha-256, etc. Para enlazar el cifrador con el proveedor de autenticación debemos declarar el bean de la clase org.springframework.security.authentication.encoding.ShaPasswordEncoder (hemos elegido el algoritmo sha) y hacer la referencia de la siguiente forma:

<bean id="passwordEncoder" 
     class="org.springframework.security.authentication.encoding.ShaPasswordEncoder"></bean>

<security:authentication-manager alias="authenticationManager">
   <security:authentication-provider user-service-ref="userDetailsManager">
      <security:password-encoder ref="passwordEncoder">
      </security:password-encoder>
   </security:authentication-provider>
</security:authentication-manager>

En el controlador también tenemos que inyectar la referencia del bean passwordEncoder para poder encriptar la contraseña correctamente:

@Autowired
private PasswordEncoder passwordEncoder;

User user = new User(username, passwordEncoder.encodePassword(password, null), 
           true, false, false, false, authorites);

Para asegurarnos de que las contraseñas se cifran realmente, vamos a crear dos usuarios y enseguida vemos como se almacenan en la base de datos:

Como se puede observar, efectivamente, las contraseñas se cifran, aunque el hash generado es idéntico en ambos casos debido a que la contraseña elegida para los usuarios es la misma (¿se atreven a descifrarla?). Los algoritmos hash son conocidos por ser deterministas, es decir, la misma entrada siempre genera la misma salida. Si un usuario malicioso se hace con las contraseñas puede realizar ataques de fuerza bruta o de diccionario, y con ello conseguir a revelar las contraseñas. Una forma de impedir esto y añadir otra capa de protección a las contraseñas cifradas es concatenar un token único a la contraseña denominado salt. Con esto podemos asegurar que los hash de dos contraseñas cifradas nunca tendrán el mismo valor. La elección del valor para el salt es importante, y suele usarse el instante de creación del usuario, valores generados de forma aleatoria, etc.

Para no complicar demasiado la implementación, vamos a escoger el nombre de usuario como valor del salt. Primero declaramos el bean del generador de salt y los enlazamos con el encriptador:

<bean id="saltSource" 
      class="org.springframework.security.authentication.dao.ReflectionSaltSource">
   <property name="userPropertyToUse" value="username"></property>
</bean>

<security:password-encoder ref="passwordEncoder">
   <security:salt-source ref="saltSource"/>
</security:password-encoder>

Este tipo de generador obtendrá el valor de salt vía reflexión, examinando el valor del atributo username del objeto User. El método de creación de nuevo usuario también lo debemos modificar añadiendo la siguiente línea:

User saltedUser = new User(username, passwordEncoder.encodePassword(password, 
        saltSource.getSalt(user)), true, false, false, false, authorites);
			
userDetailsManager.createUser(saltedUser);

El bean saltSource lo hemos inyectado en el controlador de la forma ya anteriormente conocida. Creamos de nuevo los mismos usuarios con la misma contraseña para observar el valor de hash generado:

Con esto cerramos esta entrada; pueden descargarse la aplicación artgasmator desde este enlace (fichero RAR, 400KB aprox.). La próxima vez hablaremos de algunas técnicas avanzadas de protección, filtrado de datos y renderizado condicional entre otros.

Vayan practicando, tengan paciencia y pasen un buen y largo fin de semana.

(N.d.E. No olviden de que el lunes es fiesta en Valencia y no habrá entrada.)

Spring Security – Securización básica

Como ya adelantamos en la entrada anterior, el proceso de aprendizaje y demostración de Spring Security lo llevaremos a cabo construyendo una aplicación web. Para ser más precisos, se trata de una aplicación web colaborativa que permitirá a los usuarios organizar, crear y compartir sus colecciones musicales (álbumes, discos, artistas). Además, podrán planificar y tener noticias actualizadas de los últimos eventos musicales, crear anuncios de trabajo relacionados con la música, listas de reproducción, etc. Actualmente, solo una pequeña parte de la funcionalidad anteriormente dicha está implementada, pero que iremos completando en las próximas entradas. En la siguiente imagen se aprecia la página principal de nuestra aplicación.

Se pueden diferenciar claramente tres zonas:

  • La cabecera, donde se encuentra el logo, el componente para las búsquedas y algunos botones de acceso directo.
  • El contenido principal.
  • El pie de página donde está ubicado el tablón de anuncios y paneles para acceder a páginas de competición, recomendaciones musicales, etc.

Hemos decidido usar Spring MVC por su facilidad de mapeo de peticiones y por el hecho de que tendremos a nuestra disposición todas esas marcianadas exóticas que ya comentamos: inyección de dependencia, AOP, abstracciones para el acceso a datos, gestión transparente de transacciones, etc. Sin embargo, si nuestro punto fuerte no es precisamente Spring MVC, podemos usar otro framework web, siempre y cuando se pueda integrar con Spring (la mayoría ofrecen integración). Para persistir los datos de nuestro sistema, vamos a usar el servidor de bases de datos MySQL, aunque nada nos impide usar cualquier otro sistema gestor compatible con el estándar JDBC. En un futuro, sería interesante usar algún sistema de almacenamiento de datos de la nueva generación, como MongoDB, orientado a trabajo con colecciones y modelos libres de esquema, Neo4J, base de datos basada en grafos o incluso almacenamiento en la nube.

Por último, mencionar que el motor de plantillas usado es Sitemesh por la facilidad que ofrece a la hora de definir los decoradores de contenido. También vamos a usar JQuery y un montón de plugins de este versátil framework JavaScript.

Después de presentar brevemente las tecnologías usadas, he aquí el resumen de lo que aprenderemos esta vez :

  • Configurar Spring Security.
  • Habilitar la autenticación básica.
  • Definir el gestor y proveedor de autenticación basado en memoria.
  • Declarar filtros interceptores para proteger las URL.
  • Proteger los métodos de nuestro proceso de negocio.

En primer lugar, descargamos la distribución de Spring Security desde la página del proyecto. En el momento de estar escribiendo esta entrada, la versión de producción es la 3.0.7. Una vez descomprimida, añadimos al directorio lib de nuestro proyecto las librerías que aparecen seleccionadas en la imagen.

Obviamente, necesitaremos el resto de librerías de Spring para arrancar la aplicación. A continuación hemos de declarar varios descriptores web, exactamente la clase listener (ContextLoaderListener) que cargará el contenedor y inicializará los beans, y el filtro DelegatingFilterProxy cuyo propósito es envolver y securizar todas las peticiones aplicando la cadena de filtros. Este también actúa como pasarela entre el contexto de aplicación y los servlets / filtros. Añadimos las siguientes líneas al fichero web.xml:

<filter>
   <filter-name>springSecurityFilterChain</filter-name>
   <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
   <filter-name>springSecurityFilterChain</filter-name>
   <url-pattern>/*</url-pattern>
</filter-mapping>
	
<context-param>
   <param-name>contextConfigLocation</param-name>
   <param-value>/WEB-INF/securityApplicationContext.xml</param-value>
</context-param>
	
<listener>
   <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

En el fichero securityApplicationContext.xml vamos a tener las declaraciones de los beans específicos de Spring Security. A partir de la versión 2.0.1, Spring Security ofrece el espacio de nombre security que reduce considerablemente la cantidad de beans a declarar para habilitar la funcionalidad básica. Usando los elementos del namespace anteriormente dicho, podemos habilitar la autenticación básica de la siguiente forma:

<security:http auto-config="true">
    	
</security:http>
    
<security:authentication-manager alias="authenticationManager">
    <security:authentication-provider>
    	<security:user-service>
    		<security:user name="nedo" password="nedo"   authorities="ROLE_USER"/>
    		<security:user name="admin" password="admin" authorities="ROLE_ADMIN"/>
   		</security:user-service>
   	</security:authentication-provider>
</security:authentication-manager>

Si ahora “redesplegamos” la aplicación y intentamos acceder, se nos presentará la página similar a la mostrada en la imagen:

¡Sin escribir ni una línea de código, hemos habilitado la autenticación en nuestra aplicación! Podemos intentar autenticarnos con el nombre de usuario inexistente o contraseña incorrecta, para comprobar que efectivamente el proceso de autenticación no se realizará con éxito. Por detrás, Spring Security se ha encargado de declarar una serie de artefactos:

  • AuthenticationManager, el gestor de autenticación cuya responsabilidad es validar las credenciales del usuario y enriquecer el objeto Authentication con información adicional. Este objeto normalmente contendrá el nombre de usuario, o los roles que se hayan asignado a la entidad.
  • AuthenticationProvider, que provee al gestor de autenticación de datos que éste usará para decidir si las credenciales proporcionadas por el usuario son correctas. En nuestro caso los datos de usuario se almacenarán en memoria y en texto plano, usando el elemento <security:user-service>. Cada usuario del sistema se definirá a través del elemento </security><security :user>, cuyo atributo name será el nombre de usuario, password la contraseña y el atributo authorities contendrá uno o varios roles asignados al usuario. Evidentemente para una aplicación en producción esto no nos va a servir, pero para propósitos de demostración o pruebas, de momento nos vale.

  • AbstractAuthenticationProcessingFilter, que se encargará de procesar y extraer las credenciales de usuario de la petición HTTP e inicializar el objeto Authentication.

Nuestro siguiente objetivo es securizar las URL para que solamente los usuarios con permisos adecuados puedan acceder a éstas. Para este propósito tenemos a nuestra disposición el elemento <security:intercept-url>. Queremos que únicamente los usuarios con el rol ADMIN puedan acceder a la página para la creación de géneros musicales:

<security:http auto-config="true">
	<security:intercept-url pattern="/genres/create" access="ROLE_ADMIN"/>
	<security:intercept-url pattern="/*" access="ROLE_USER, ROLE_ADMIN"/>
</security:http>

Aquí hemos declarado dos reglas de intercepción dentro del contexto del elemento <security:http>. Como se puede apreciar, el atributo pattern define el patrón de la URL a proteger, y access serán uno o varios roles a los que se otorgará el acceso. Es importante el orden en el que se definen las reglas, por lo tanto las más específicas las pondremos al principio. Si ahora nos autenticamos con el usuario nedo (que tiene el rol USER),y intentamos crear un nuevo género, se nos denegará el acceso mostrando la siguiente página:

En las sentencias de intercepción podemos usar el lenguaje de expresiones de Spring (SpEL), para construir reglas más complejas mediante el uso de métodos, propiedades, etc. Para habilitar dichas expresiones, tenemos que inicializar el atributo use-expressions del elemento <security:http>:

<security:http auto-config="true" use-expressions="true">
    <security:intercept-url pattern="/genres/create" access="hasRole('ROLE_ADMIN')"/>
    <security:intercept-url pattern="/*" access="hasAnyRole('ROLE_ADMIN','ROLE_USER')"/>
</security:http>

Aquí podemos observar el uso de los métodos hasRole y hasAnyRole gracias al lenguaje de expresiones. Para la lista completa de métodos y pseudo propiedades disponibles, podemos consultar la referencia de Spring Security.

El filtro FilterSecurityInterceptor delega la responsabilidad al gestor de decisión AccessDecisionManager para decidir que petición se aceptará. Teniendo en cuenta el contexto de la petición y las autoridades del usuario, otro componente llamado Voter (votante), puede emitir el voto de acceso concedido, acceso denegado o abstenerse si no dispone de información suficiente. En el caso del elemento <security:intercept-url> el votante recibe la información del contexto de la petición a través del atributo access. La implementación por defecto del gestor de decisión en Spring Security es AffirmativeBased, por lo tanto con que uno de los votantes emita el voto de acceso concedido el acceso al recurso protegido se permitirá. En las próximas entradas veremos como alterar este funcionamiento, proporcionando la declaración del gestor de decisión y votantes.

En este punto, nuestra aplicación ha pasado de estar totalmente desprotegida a disponer del mecanismo de autenticación y concesión de accesos a los recursos en función de los privilegios del usuario. Pero, aun así, si el usuario malicioso consigue penetrar la capa web de nuestra aplicación puede comprometer la seguridad global de la misma. Necesitamos añadir otra capa de protección, y securizar la invocación de métodos de nuestro proceso de negocio. Como ya dijimos, Spring Security permite proteger la invocación de métodos, bien mediante anotaciones o declaración de puntos de corte. En esta ocasión vamos a ver como proteger los métodos usando la anotación PreAuthorize. Los usuarios con el rol ADMIN podrán listar, borrar o modificar los géneros musicales, pero el resto de usuarios tan solo podrá visualizar los géneros. En la imagen se muestra el aspecto de la página:

El primer paso consiste en habilitar las anotaciones de autorización:

<security:global-method-security pre-post-annotations="true" />

Es importante que la declaración del elemento anterior se haga en el contexto de aplicación raíz, es decir donde tengamos definida la referencia del bean AlbumGenreService. La anotación la vamos a colocar sobre el método en la interfaz del servicio:

public interface AlbumGenreService {
	@PreAuthorize("hasRole('ROLE_ADMIN')")
	public void deleteGenre(Integer genreId);
}

De esta forma el método solamente se podrá invocar si el usuario dispone del rol ADMIN. Podemos hacer la prueba, entrando otra vez con el usuario nedo, e intentar eliminar un género. El resultado se puede ver en la imagen:

En esta entrada se han visto los conceptos básicos, pero importantes para seguir con soltura el resto de entradas. Se recomienda al lector la descarga de la aplicación web [artgasmator] para estudiar bien su arquitectura y todos los aspectos anteriormente vistos.

La próxima vez veremos cómo integrar la página de login y logout con el look & feel de nuestra aplicación, implementar el renderizado condicional, la gestión de usuarios e almacenamiento y protección de las credenciales en las bases de datos relacionales.

Hasta entonces, ¡happy spring-securing!

Spring Security – Introducción a la seguridad declarativa

Esta va ser la primera de las n entradas que vamos a dedicar a Spring Security. Para los que todavía no lo conocen, Spring Security es uno de los framework de seguridad J2EE más populares y completos usado por parte de instituciones, empresas e universidades muy importantes. Con Spring Security es bastante trivial incorporar la infraestructura básica de seguridad en una aplicación ya implementada sin necesidad de escribir ni una línea de código gracias a su naturaleza no invasiva. Además, posee componentes ya implementados que permiten la integración con sistemas externos con mucha facilidad. Inicialmente fue basado en el proyecto Acegi Security, que con los años se fue convirtiendo en un producto robusto y maduro, actualmente miembro de la familia de productos Spring. Debido a que muchos de los conceptos como la inyección de dependencias y programación orientada a aspectos los hereda de Spring, es aconsejable que tengamos nociones básicas de las mismas.

La seguridad de la aplicación en sí se considera como un requisito transversal, y por lo tanto se puede encapsular en forma de aspecto y más tarde aplicarse de forma declarativa en cualquier punto de nuestra aplicación. Spring Security ofrece módulos que aíslan ese tipo de lógica, permitiendo además que la aplicación tenga una arquitectura robusta, limpia y débilmente acoplada.

Otro de los elementos que ayuda a que los componentes de nuestra aplicación tengan acoplamiento bajo es el patrón de diseño llamado inyección de dependencias. En vez de que las clases se encarguen de obtener sus propias referencias, éstas son instanciadas e inyectadas desde una entidad externa llamada contenedor. El bajo acoplamiento también se consigue con las interfaces. Es importante que los objetos conozcan sus referencias a través de las interfaces, ya que se consigue ocultar la implementación y cambiarla sin necesidad de tocar la referencia. Para cualquier proyecto no trivial es casi un patrón de uso obligatorio. Para más información se puede consultar el manual de Spring.

Como esta entrada va a ser mera introducción teórica, no vamos a entrar en los detalles de configuración y puesta en marcha del framework, sino simplemente conocer la arquitectura básica y todas las posibilidades que nos ofrece.

Spring Security trata dos aspectos de seguridad fundamentales: autorización e autenticación. Para eso ofrece toda una serie de filtros que se encargan de interceptar las peticiones HTTP y aplicar procesamientos de índole variada antes de que estas alcancen el destino final. Como símil pensemos por un momento en la cebolla y las capas que la forman: para llegar al corazón es necesario quitar todas las capas. De la misma forma, la petición tiene que pasar por toda la cadena de interceptores antes de alcanzar el destino. En la plataforma J2EE los filtros son artefactos representados por la interfaz javax.servlet.Filter, y a menudo combinados en la cadena de filtros a través de la interfaz javax.servlet.FilterChain siguiendo un orden determinado. Así, por ejemplo, antes de que al usuario se le acceda acceso a un recurso determinado como puede ser una página o invocación a un método, el primer filtro de la cadena se encargará de comprobar si el usuario está autenticado en el sistema. El siguiente paso consiste a partir de la entidad determinar si el usuario tiene permisos suficientes para acceder al recurso, etc. Spring Security ofrece su propia abstracción de la cadena de filtros, permitiendo crearlo dinámicamente a través del fichero de configuración como veremos en las entradas siguientes.

La protección de los métodos de la capa de negocio se realiza a través de aspectos y se puede securizar la invocación al método, antes y después. Las llamadas a los métodos se protegen utilizando anotaciones Java estándar, o bien mediante declaraciones de puntos de corte para la protección global de los servicios.

Para el proceso de autenticación tenemos a nuestra disposición un gran número de mecanismos, y además la posibilidad de extender el propio framework y crear nuestro propio proveedor de autenticación. Los datos que precisa el proveedor para la correcta autenticación de los usuarios se pueden almacenar en memoria (útil en las fases de desarrollo o pruebas sencillas), bases de datos relacionales, repositorios LDAP, sistemas OpenID o Kerberos entre otros. Para el usuario queda totalmente transparente la forma de autenticación, por lo que en una aplicación funcionando en entorno de producción podemos cambiar el tipo de autenticación sin tocar el propio código.

Las posibilidades de Spring Security no acaban aquí. En los proveedores de autenticación que utilizan las bases de datos relacionales es posible incrementar el nivel de protección de las contraseñas a través de algoritmos de encriptación adicionales. Además, ofrece técnicas para el control de sesiones y nos protege de ataques que pueden provocar el robo de las mismas. Para algunos servicios como el remember me, login o logut, dispone de componentes ya implementados y listos para usarlos en nuestras aplicaciones.

En la siguiente entrada veremos como configurar el framework y crear una aplicación sencilla. Además entraremos en más detalle con las clases involucradas en el proceso de autenticación e autorización. Manténganse a la escucha.