RSS 2.0 Atom 1.0 CDF  
JASoft.org - Cómo evitar que la sesión caduque por inactividad
El blog de José Manuel Alarcón Aguín. Programación .NET y mucho más...
 

Siguiendo con esta mini-serie (parte I y parte II) sobre el mantenimiento de las sesiones en ASP.NET, en esta ocasión me voy a centrar en cómo evitar que una sesión caduque por inactividad mientras el usuario tenga el navegador abierto. Esta situación puede ser deseable en muchas ocasiones.

Por ejemplo, nada fastidia más que estar redactando en el navegador directamente un artículo para un boletín o un blog (como es el caso) y que cuando vas a enviarlo y pulsas el botón "Publicar" te salga un mensaje diciéndote que la sesión ha caducado (y por supuesto has perdido todo lo que escribiste). En este caso es interesante que la página de edición mantenga la sesión viva mientras tú estás trabajando en ella, aunque te vayas a comer y vuelvas dos horas más tarde. Este caso se da por ejemplo en aplicaciones como nuestro servicio MAILCast®, en el que hemos implementado una solución similar a la que describiré ahora.

Otro ejemplo lo tenemos en servicios como nuestra plataforma de teleformación SELF, con la que impartimos los cursos de campusMVP. Si un alumno está parado en una lección que contiene por ejemplo un vídeo cuya duración es de media hora (la sesión normal dura 20 minutos) o que tiene un laboratorio que va siguiendo paso a paso y tarda una hora en hacerlo, cuando finalmente quiera pasar al siguiente punto del índice se encontraría con la desagradable sorpresa de que la sesión ha caducado y que por lo tanto debería volver a autenticarse en el sistema. Esto no es aceptable claramente, así que tenemos que buscar una solución para evitarlo.

Lo primero que se le suele ocurrir a todo el mundo es aumentar el tiempo de caducidad de sesión bien tocando la configuración de IIS (mala cosa pues afecta a toda la aplicación) o bien aumentándolo para una página específica. El probvlema que tiene esto es que nunca sabes cuánto es un tiempo razonable y que además en muchas ocasiones variará enormemente de unas páginas a otras al ofrecer éstas contenido dinámico. Además si luego realmente el usuario no permanece en la página y se limita a cerrar el navegador, la sesión se quedará ocupando memoria y recursos del servidor durante mucho rato sin necesidad alguna. Así que alguna forma mejor tiene que haber...

Lo cierto es que conseguirlo es muy fácil. ¿Cómo se consigue de manera natural que una sesión se mantenga activa?, pues como sabemos recibiendo peticiones del usuario en el servidor, las cuales renuevan el periodo de caducidad en cada petición.

Pues entonces la solución está clara: vamos a lanzar peticiones al servidor en segundo plano usando JavaScript, de forma que se reciban en el servidor y mantengan la sesión activa. Ni más ni menos.

Para ello una forma habitual de hacerlo sería usar el objeto XmlHttpRequest en el que se basan las aplicaciones AJAX, si bien como veremos enseguida ni siquiera hace falta complicarse tanto.

El "truco" es que el recurso que llamemos en el servidor no puede ser cualquiera, sino que tiene que ser alguno que pase por el "pipeline" de ASP.NET que incluya gestión de sesiones. Lo mejor, por tanto, es llamar a una página .ASPX o a un manejador .ASHX.

La llamada la podemos hacer usando un simple script que solicite más script al servidor, con un código similar a este:

//Ejecuta el script en segundo plano evitando así que caduque la sesión de esta página
function MantenSesion() 
{                
    var CONTROLADOR = "refresh_session.ashx";
    var head = document.getElementsByTagName('head').item(0);            
    script = document.createElement('script');            
    script.src = CONTROLADOR ;
    script.setAttribute('type', 'text/javascript');
    script.defer = true;
    head.appendChild(script);
} 

Con esto lo que hacemos es crear un nuevo script en la cabecera de la página y añadirlo al DOM del navegador, momento en el cual se solicitará al servidor. El origen del script en este caso es un simple manejador ASHX (que debe implementar la interfaz IRequiresSessionState para que se tenga acceso a la sesión y por lo tanto entre en el proceso el proveedor de sesiones) y que devuelve en este caso un simple comentario de script, ya que lo único que buscamos es que se mantenga activa la sesión gracias a la petición.

Podemos conseguir algo similar usando un iframe oculto al que mediante script le cambiamos la propiedad "src" para que pida una página ASPX del servidor la cual se devuelve vacía pero nos sirve para el mismo propósito. Es decir, no es necesario recurrir a técnicas más sofisticadas como la de XmlHttpRequest y nos aseguramos que funcionará hasta en navegadores bastante antiguos.

La otra cosa que hay que considerar es el intervalo de tiempo que usaremos para llamar a esta función de JavaScript que mantendrá la sesión viva. Si la sesión dura 20 minutos podemos llamar cada 18 minutos o así (para asegurar) y así no impactará demasiado en el servidor que mandemos estas peticiones extra pues son muy escasas. Por lo tanto la mejor forma de proceder es establecer un temporizador que llame a dicha función por ejemplo cada el 90% de la duración de la sesión de ASP.NET. Se consigue de forma muy sencilla simplemente añadiendo un código como este al final de nuestra página ASPX:

<script language="javascript" type="text/javascript">
    setInterval('MantenSesion()', <%= (int) (0.9 * (Session.Timeout * 60000)) %>);
</script>

Así lo que conseguimos es crear un temporizador que se repetirá cada 'x' milisegundos y que llamará a la anterior función para mantener la sesión abierta. Se multiplica por 60.000 el tiempo de sesión porque éste está expresado en segundos y el intervalo del temporizador debe ir en milisegundos. En el caso del tiepo de sesión por defecto (20 minutos) este código generaría un temporizador que renovaría la sesión cada 1.080.000 milisegundos, o sea, cada 18 minutos.

Como vemos es muy fácil de conseguir y puede resultar muy útil en ocasiones.

¡Espero que te sirva!

Wednesday, June 11, 2008 10:00:35 PM (Hora de verano romance, UTC+02:00)  #    Comments [10]   ASP.NET  |  Trackback
Monday, June 16, 2008 12:19:42 PM (Hora de verano romance, UTC+02:00)
Hola, muy interesante !!!

Cuál es el contenido exacto de refresh_session.ashx ??

Para ciertas funcionalidades, por ejemplo GMail, guarda un borrador cada cierto tiempo del mensaje que desea enviar por correo

Saludos.
espinete
Monday, June 30, 2008 11:42:50 AM (Hora de verano romance, UTC+02:00)
Hola José Manuel

La verdad es que esta utilidad puede resultar realmente itneresante porque empieza a ser un quebradero de cabeza el tema de la inactividad de la sesión. Por cierto ¿sabes de algún documento o artículo que pueda explicar un poquito las implicaciones de hacer cambios en los timeout bien a nivel de IIS o de aplicación?. Lo digo porque resulta que hago cambios en mis aplicaciones y a veces los timeout no se modifican de acuerdo al valor que pongo y supongo que habrá algún elemento en el s.o. que quizás haga que eso sea así.

Muchas gracias
Javier
Monday, June 30, 2008 12:07:01 PM (Hora de verano romance, UTC+02:00)
Si cambias el Timeout d elas sesiones te tiene que funcionar, bien por código o bien por configuración de IIS. Eso sí, tiene un límite máximo de 24 horas. A lo mejor es eso...

Saludos

JM
Monday, July 21, 2008 1:02:47 PM (Hora de verano romance, UTC+02:00)
Hola José Manuel

Estaba probando la utilidad para la no caducidad de la sesión y la verdad es que va muy bien. Tengo una pequeña duda. ¿Este tipo de ejecución tiene alguna limitación en cuanto a recursos consumidos o algún aspecto a tener en cuenta?. Intuyo que no. Y si no los tiene imagino que sería factible por ejemplo crearse una clase nueva que herede de System.Web.UI.Page que lo hiciera automáticamente. Perdona la pregunta pero es que me parece tan sencillo que pienso que algún truco o problema debe haber.

Gracias y un saludo
Javier
Monday, July 21, 2008 1:13:43 PM (Hora de verano romance, UTC+02:00)
Javier:

La única limitación es que cuando mantienes la sesión activa estás mandando peticiones extra que quieras que no cargan más el servidor (aunque si las espacias lo suficiente no tienen porqué incidir casi en el mismo). Por otro lado mantienes en memoria las vaiables de sesión, pero vamos, lo mismo que si la mantuvieses por timeout y no por hacer peticiones.

Asi que no, no hay problemas adicionales y bien puedes hacer lo que dices y meterlo en una clase base de la que heredar tus páginas que no caducan :-)

Saludos

JM
Tuesday, August 05, 2008 3:05:15 PM (Hora de verano romance, UTC+02:00)
Hola Jose Manuel,

nunca he creado un manejador y no entiendo muy bien como hacerlo. He creado uno y me da errores. ¿Podrías explicarme un poquillo como hacerlo?

He intentado de todo para solucionar lo de las sesiones y no hay manera.

Un saludo,
Monica
Wednesday, August 06, 2008 7:43:46 AM (Hora de verano romance, UTC+02:00)
Monica:

no te preocupes, en lugar de usar un manejador usa una página cualquiera. Con que crees una página ASPX en blanco y la llames desde el javaScript de cliente sería suficiente. Lo del manejador es por añadir eficiencia, pero no es necesario en absoluto.


Saludos

JM
Wednesday, August 13, 2008 11:07:12 PM (Hora de verano romance, UTC+02:00)
Tengo un problema con la caducidad de las sessiones y por lo que veo, este ejemplo que pones no se mas o menos como utlizarlo no si podrias mandarme un ejemplo para ver mas o menos como funciona realmente te lo agredeceria muchisimimo.
Gabriel Euan
Saturday, September 13, 2008 12:39:01 AM (Hora de verano romance, UTC+02:00)
Hola Juan, no logro implementar tu código ni con un handler ni con el web form.
Abraham Picon
Saturday, September 13, 2008 1:59:16 AM (Hora de verano romance, UTC+02:00)
Hola ya me funciona la compilación y la ejecución más sin embargo la sesión aún caduca.
Qué crees que podría estar pasando Manuel?
Josue
Name
E-mail
Home page

Comment (Some html is allowed: a@href@title, b, i, strike, strong, u) where the @ means "attribute." For example, you can use <a href="" title=""> or <blockquote cite="Scott">.  

Enter the code shown (prevents robots):

Live Comment Preview
Copyright © 2008 José Manuel Alarcón Aguín. All rights reserved.