El estado de la sesión son datos que capturan el estado actual de la interacción del usuario con aplicaciones tales como un sitio web o un juego.
Una aplicación web típica mantiene una sesión abierta para cada usuario conectado, durante todo el tiempo que el usuario esté ahí. El estado de la sesión es la forma en que las aplicaciones recuerdan la identidad del usuario, las credenciales de inicio de sesión, la información de personalización, las acciones recientes, el carrito de la compra, etc.
La lectura y escritura de los datos de la sesión en cada interacción con el usuario pero deben hacerse sin perjudicar la experiencia del usuario. En segundo plano, el estado de la sesión son datos almacenados en caché para un usuario o una aplicación específicos que permiten poder responder con rapidez a las acciones del usuario. Por eso, mientras la sesión del usuario esté activa, no es necesario salir y acceder constantemente a la base de datos central.
El último paso en el ciclo de vida del estado de la sesión se produce cuando se desconecta el usuario. Algunos datos se mantendrán en la base de datos para su uso futuro, pero la información transitoria se puede descartar una vez finalizada la sesión.
Comprender las mejores prácticas del estado de la sesión es clave para evaluar y resolver los problemas comunes relacionados con las sesiones, como el aislamiento, la volatilidad y la persistencia.
Mientras la sesión está activa, la aplicación lee y escribe exclusivamente en el almacén de sesiones en memoria. Es decir, que las operaciones de escritura son más rápidas, pero también que no hay tolerancia a la pérdida de datos. Debido a que los datos del almacén de sesiones no son una simple instantánea de otra base de datos, deben ser muy duraderos y estar siempre disponibles.
El estado de la sesión es similar a una caché, pero tiene diferentes ciclos de vida de lectura/escritura. Veamos: una caché tolera la pérdida de datos y puede restaurarse en cualquier momento a partir de una base de datos primaria. Escribir en una caché también requiere escribir en la base de datos subyacente. Por el contrario, el estado de la sesión solo se puede restaurar desde la fuente de datos primaria cuando se inicia la sesión del usuario y persiste de nuevo hasta la fuente cuando finaliza la sesión.
El estado de la sesión puede ser volátil o permanente, lo que significa que los datos pueden ser descartados o persistir en el almacenamiento en disco cuando finaliza la sesión del usuario. Un ejemplo de datos de sesión volátiles podría ser el historial de navegación de una página en una intranet corporativa: no es necesario conservarlo. En cambio, un carrito de la compra en una aplicación de comercio electrónico que dure es fundamental para el negocio y debe guardarse en un almacén permanente.
El estado de la sesión se almacena como un par clave-valor con el identificador del usuario como clave y los datos de sesión como valor. De este modo se garantiza que las sesiones de los usuarios no puedan acceder a otra información.
El almacenamiento del estado de la sesión en una caché rápida en memoria permite algunos escenarios analíticos en línea que, si no, penalizarían a una base de datos transaccional. Estas aplicaciones incluyen análisis y paneles en tiempo real, motores de recomendación y detección de fraudes.
Consideremos una aplicación de chat de texto que utiliza MySQL como base de datos relacional, Node.js como tecnología de servidor backend y Redis Enterprise para la gestión de sesiones. El frontend se compone de dos páginas: una página de inicio, en la que se registran los usuarios, y una página de chat, en la que los usuarios escriben y envían mensajes.
Para mayor simplicidad, solamente mostraremos el código del servidor. Se explicará cómo implementar el ciclo de vida del estado de la sesión en Node.js. Omitiremos también las páginas de vista HTML y el resto de la aplicación.
En primer lugar, la aplicación carga las dependencias, incluida la sesión, los objetos Redis y el cliente MySQL:
var express = require("express"); var session = require('express-session'); var mysql = require("mysql"); var redis = require("redis"); var redisStore = require('connect-redis')(session); var redisClient = redis.createClient(); // aquí se cargan más dependencias ...
Estas declaraciones crean objetos para gestionar el enrutamiento web, la sesión, la base de datos, el almacenamiento en caché y las bibliotecas Node.js de sesión. A continuación, configura Redis como almacén de sesiones:
app.use(session({ secret: 'mysecret', // crear nuevo almacén redis. store: new redisStore({ host: 'localhost', port: 6379, client: redisClient }), saveUninitialized: false, resave: false }));
A continuación, configura las rutas exprés de Node.js para la página de inicio y del chat, junto con el soporte para las solicitudes AJAX procedentes del cliente, incluidos el inicio de sesión, el cierre de sesión y el envío de comentarios.
Cuando un usuario solicita la página de inicio, el servidor lo redirige a la página chat.html o muestra la página de inicio de sesión.html, dependiendo de si el usuario está conectado o no. El siguiente fragmento te muestra el código del controlador para la ruta web /get:
app.get('/',function(req,res){ // crear nuevo objeto de sesión. if(req.session.key) { // el usuario ya ha iniciado sesión res.redirect('/chat'); } else { // no se ha encontrado ninguna sesión, vaya a la página de inicio res.render("login.html"); } });
Cuando el usuario envía los datos del formulario de acceso (con el correo electrónico y la contraseña), el cliente JavaScript AJAX envía los datos del formulario al servidor. En este ejemplo, activa la función executeLoginDbCommand (no se muestra aquí), que ejecuta una consulta SQL contra la base de datos MySQL y devuelve un objeto que contiene los datos de la sesión del usuario previamente guardados.
Si el inicio de sesión se realiza correctamente, los datos del usuario procedentes de MySQL se guardan en la sesión web respaldada por el almacén de sesiones de Redis, y el código JavaScript del cliente redirige al usuario a la página de chat:
app.post('/login',function(req, res){ // SQL Query comparará los datos de inicio de sesión y la contraseña // desde el cuerpo de la solicitud HTTP a los datos de la tabla de usuarios executeLoginDbCommand(req.body.Email, req.body.Password, function(dbResult){ // if(!dbResult) { res.json({"success" : false,"message" : "Login failed ! Please register"}); } else { req.session.key = dbResult; res.json({"success" : true,"message" : "Login success."}); } }); });
La página de chat de la aplicación permite a los usuarios leer y enviar mensajes a otras personas conectadas a la aplicación. Dado que los usuarios solo ven sus propias interacciones de mensajes con otras personas, los datos devueltos por el servidor para las solicitudes de la página de chat cambian de un usuario a otro. Y la cosa más importante, el acceso a esta página está restringido exclusivamente a los usuarios registrados. La comprobación de la clave de sesión revela si el usuario está conectado o no:
app.get('/chat',function(req,res){ if(req.session.key) { //el usuario ya ha iniciado sesión, //pasemos a la página del chat con el correo electrónico del usuario res.render("chat.html", { email : req.session.key["UserEmail"], name : req.session.key["UserName"] }); } else { // no se ha encontrado ninguna sesión, vaya a la página de inicio res.redirect("/"); } });
Cuando el usuario envía un nuevo comentario desde la página de chat, el cliente JavaScript AJAX envía los datos del formulario al servidor. Si el usuario está conectado, los comentarios se insertan en la tabla MySQL UserComments. Para ello, usa la función executeSendCommmentDbCommand (no se muestra aquí).
app.post("/sendComment",function(req,res){ // This SQL command will insert a new comment in // users table app.post("/sendComment",function(req,res){ // Este comando SQL introducirá un nuevo comentario en // tabla de usuarios if(req.session.key) { executeSendCommmentDbCommand(req.body.Email, req.body.Recipient, req.body.Comment, function(dbResult){ if(!dbResult) { res.json({"success" : true, "message" : "Comment has been sent successfully."}); } else { res.json({"success" : false, "message" : "SendComment failed!"}); } }); } else { res.json({"success" : false, "message" : "Please login first."}); } });
When the usCuando el usuario cierra la sesión, el objeto de sesión se destruye y el usuario es redirigido a la página de inicio de sesión. Pero antes, la función executePersistSessionDbCommand (no se muestra aquí) guarda la sesión de usuario en memoria en la base de datos MySQL:
app.get('/logout',function(req,res){ // el usuario ya ha iniciado sesión, borremos la sesión // y redireccionar a la página de inicio de sesión. if(req.session.key) { executePersistSessionDbCommand(req.session, function(dbResult){ if(!dbResult) { req.session.destroy(function(){ res.redirect("/"); } else { res.json({"success" : false, "message" : "Session persistence failed!"}); } }); }); } else { res.redirect("/"); } });
Estos fragmentos muestran de modo superficial una aplicación real que utiliza Redis como almacén de sesiones. Esto nos sirve para mostrar cómo Redis puede gestionar el ciclo de vida del estado de la sesión en memoria en combinación con el almacenamiento de bases de datos permanentes como MySQL.