Datos de sesión externalizados con Spring Session
Escrito por
el , actualizado el .
java
planeta-codigo
programacion
spring
Enlace permanente
Comentarios
Por defecto los datos de la sesión de una aplicación web Java se guardan en el servidor de aplicaciones y en memoria, esto produce que al reiniciar el servidor por un despliegue los datos de la sesión se pierdan y provoque en los usuarios alguna molestia como tener que volver a iniciar sesión. En Tomcat existe la posibilidad de que los datos de las sesiones sean persistidas en disco con la opción saveOnRestart del elemento de configuración Manager que evita que los datos de las sesiones se pierdan en los reinicios, al menos para los servicios formados por una única instancia. Para evitar que los usuarios perciban los reinicios o caídas del servidor hay varias soluciones algunas tratando de diferentes formas externalizar las sesiones del servidor de aplicaciones. Con estas soluciones se pueden hacer despliegues sin caídas, sin que las perciban los usuarios, siendo útil para hacer actualizaciones frecuentemente, continuos, y en cualquier momento cuando tengamos una nueva versión de la aplicación.
Las soluciones más comentadas son:
- Cluster de servidores: para evitar las caídas podemos formar un cluster de máquinas de forma que si una se reinicia las peticiones sean atendidas por el resto de servidores del cluster. Añadiendo una poca configuración se puede formar un cluster de servidores Tomcat. Si el cluster está formado por unos pocos servidores esta solución es válida pero si el cluster es grande (¿media docena de máquinas?) el tráfico que se genera para sincronizar los datos de sesión en todas las máquinas puede ser significativo, momento en el cual se opta por otras soluciones.
- Sesión en base de datos relacional: los datos de la sesión se pueden guardar en una base de datos relacional, al llegar una petición al servidor se recupera de la base de datos la sesión con una consulta y al finalizar la petición se lanza otra consulta de actualización. En las aplicaciones la base de datos suele ser un cuello de botella prefiriéndose guardar la sesión en otro servidor que no sea el servidor de base de datos para no generarle más carga.
- Caché externa: en esta opción los datos se guardan en un servidor externo al servidor de aplicaciones de forma que todos los servidores del cluster las compartan pero no en la base de datos relacional, algunas opciones que se pueden utilizar son memcached o Redis que almacenan los datos en memoria y son muy rápidas. Esta opción añade una pieza más a la infraestructura de la aplicación que hay que mantener. En este artículo pondré un ejemplo usando esta opción utilizando Spring Session y un servidor Redis.
- Sesión en cookie: para no añadir una pieza más a la infraestructura del servidor se puede externalizar la sesión en el cliente mediante una cookie. Como la cookie es enviada por el navegador cliente en cada petición el servidor puede recuperar los datos de la sesión. Sin embargo, como los datos son guardados en el cliente los datos de la cookie han de ser cifrados y firmados digitalmente para evitar problemas de seguridad ante modificaciones de los datos. También deberemos evitar guardar muchos datos y tendremos cierta limitación para que la cookie no sea grande, el tamaño recomendado no exceder es 4096 bytes si lo hacemos puede que ocasionemos errores con el mensaje 400 bad request, request header or cookie too large y consuma mucho ancho de banda, hay que tener en cuenta que las cookies son enviadas en cada petición al servidor origen no solo para las peticiones dinámicas sino también para los recursos estáticos como imágenes u hojas de estilos, si las cookies son grandes y el número de usuarios también el ancho de banda consumido por las cookies puede ser significativo, en estos últimos casos empleando un CDN puede aliviarse el tráfico generado. En la siguiente página están recogidos los límites de las cookies para cada navegador y el número máximo por dominio.
Usando Spring Session se puede externalizar los datos de la sesión en un servidor Redis usándolo como caché externa. Para demostrar y enseñar el código necesario he creado una pequeña aplicación web con Spring Boot. El controlador no tiene nada especial, obtiene la sesión y guarda los datos enviados en un formulario en la sesión, luego esta transparentemente se serializa en Redis. Usando la anotación @SpringBootApplication con la autoconfiguración se activa la infraestructura necesaria en el contenedor de Spring para guardar los datos de la sesión en Redis incluida la conexión a Redis. Por supuesto hay que añadir las dependencias necesarias al proyecto entre ellas el cliente Java de Redis.
|
|
|
|
|
|
|
|
Descargado el código fuente de la aplicación de ejemplo y utilizando Docker para iniciar el servidor Redis se puede iniciar la aplicación con el comando:
|
|
|
|
Lanzando una petición se puede ver como el Redis se guardan los datos de la sesión. Deteniendo la aplicación e iniciándolo de nuevo los datos de la sesión no se pierden al estar persistidos en Redis, el navegador envía la cookie de sesión que contiene únicamente su identificativo y la aplicación recupera los datos de Redis.
Examinando los datos en Redis se puede ver que se ha creado una clave con el mismo identificativo de la cookie de sesión, en la clave están guardados los valores serializados entre ellos el nombre del atributo y su valor y otros datos como la fecha de creación, el último acceso y el intervalo máximo de inactividad antes de la expiración.
En el momento de escribir este artículo Spring Session es un proyecto reciente y solo soporta la opción de Redis como caché externa pero seguramente con nuevas versiones soporte otras opciones como memcached, guardar la sesión en una cookie o en una base de datos relacional. La solución propuesta por Spring Session es válida para cualquier servidor de aplicaciones ya que se basa en crear un filtro en la aplicación que proporciona una versión modificada de HttpSession mediante el cual se guardan los datos de forma externa.
Otras posibilidades ofrecidas por Spring Session son múltiples sesiones en la misma instancia del navegador y soporte para aplicaciones REST y WebSocket. Para aumentar la seguridad se puede aumentar el tamaño del identificativo de la sesión almacenada en su cookie.