Cómo evitar que la sesión caduque por inactividad

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!


ACTUALIZACIÓN DÍA 5/5/2009


He añadido aquí, un vídeo práctico y código de ejemplo de cómo implementar esto.

Sin categoría

43 thoughts on “Cómo evitar que la sesión caduque por inactividad

  1. Me averguenzo de reconocerlo, pero este “truquillo” no se me ha ocurrido en los años que llevo pegandome con las caducidades de las sesiones.

    Mil gracias y enlazo la entrada desde mi blog.

  2. Hola:

    El contenido del ASHX podría estar en blanco como cmento en el post. Lo úni oque tendrá que implementar la intetrfaz IRequiresSessionState para que se mantenga la sesión.

    Si añades un nuevo .ashx a tu proyecto de VS2008 sólo tienes que ponerle IRequiresSessionStatea continuación de IHttphandler, así:

    public class MantenSesionHandler : IHttpHandler, IRequiresSessionState
    {
    //aquí el código del manejador, que esencialmente estará vacío.
    }

    saludos

    JM

  3. Hola muchas gracias por este aporte, soy novatillo y C# todavía no lo controlo he intentado pasar la función a VB y me da errores no lo consigo. Alquien que pueda convertir el ejemplo de C# a VB muchas gracias.

  4. Hola Roberto:

    El código del post no está en C# sino en JavaScript ya que forma parte del lado de cliente, es decir, que va metido en el HTML, así que no lo tienes que traducir.

    Saludos

    JM

  5. Muchas gracias José,

    Y para la página ASHX como pongo esto en VB

    public class MantenSesionHandler : IHttpHandler, IRequiresSessionState

    {

    //aquí el código del manejador, que esencialmente estará vacío.

    }

    De nuevo gracias.

  6. Simplemente añade un nuevo manejador genérico (que tiene la extensión .ashx) desde Visual Studio, y en el lenguaje escoge VB. Ya te viene todo hecho. Lo único que tienes que añadirle es que implemente la interfaz IRequiresSessionState, así que nada le pones a continuación de la definición de la clase esto:

    Implements IRequiresSessionState

    y listo.

    Además, dado que no tienes que escribir código dentro de él te vale también en C# aunque no controles el lenguaje.

    Saludos

    JM

  7. Muchísimas gracias José.

    Por cierto creo que das clases, me podrías decir si son en alguna academia de madrid para llamar y pedir presupuesto de los cursos que tenga.

    Me interesaria alguno de aplicaciones web y si es en C# desde el principio mejor.
    No se si esto se podrá poner aquí, sino me lo dices y te envio mi dirección de correo.

  8. he querido implementar lo indicado en este post para hacer que la session nuncac caduque, pero tengo problemas con el siguiente codigo:

    me reclama por “<%" y me reclama por "Session.Timeout" espero me puedena ayudar gracias atte Eduardo Baeza O.

  9. Marcelo:

    En VS2003 sí hay .ashx lo que pasa es que no te sale en la lista de archivos disponibles, pero funcionan igual.

    De todos modos no te hace falta que sea un.ashx: puede ser cualquier archivo gestionado por ASP.NET y que soporte sesiones, por ejemplo un .aspx normal.

    Eduardo:

    Ese código tiene que estar dentro de una página .asp o .aspx para que te funcione el código.

    saludos

    JM

  10. Jose:

    Entiendo que los dos código, el de la funcion y el de setInterval(), deben ir en el codigo del lado cliente, es ahí justamente donde me reclama lo que indique.
    Por el “< %" dice: "Warning 38 Expected expression" Consulta cuando te refieres a colocar el codigo al final de la pagina es despues de ?

    Atento a sus comentarios

    Gracias

    Saludos cordiales

    EB

  11. José

    Finalmente me resulto lo de la llamada al server para mantener la session activa, con el metodo que describes

    Muchas Gracias

    Saludios cordiales
    EB

  12. He seguido los pasos q se describen…puse el javascript en mi pagina y el xml al final de la misma y tambien he creado mi clase ashx sin contenido..pero no funciona…se pierden las variables de sesion que he creado..como puedo hacer en este caso?Gracias

  13. Jenny:

    recuerda que tu ASHX 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.

    Prueba con una página ASPX vacía y a ver si así te funciona.

    Saludos

    JM

  14. Hola José,

    Estoy teniendo problemas para implementar tu solución al problema de las sesiones.

    He realizado todos los pasos, pero me da un error en JS al hacer la llamada al .appendChild(script); Sólo me dice que hay un error, pero no me da más datos. He depurado con el vs2008 y sólo me salta el error ese que es como un alert y que pone: A runtime error….. Line 1 Error expected: ‘;’.

    Podrías ayudarme,

    Gracias y un saludo,
    Antonio

  15. Hola Antonio:

    Tiene pinta de ser que el JS que estás intentando cargar tiene un error en el JavaScript, por eso te dice eso.
    No le metas las etiquetas de

  16. Hola José, saludos desde Perú, muchisimas gracias por tu aporte lo acabo de poner en práctica y funciona a las mil maravillas.

    Sigue así para que enriquecernos aún más con tus conocimientos.

  17. pues yo tambien he tratado de implementarlo tanto con el manejador ashx como con un apagina aspx, en caso de ambos me marca un error a la hora de que se realiza el append, y el manejador no me acepta el IRequiresSessionState aun usando la libreria correspondiente, saludos

  18. aprovechando los posts, tengo el pequeño problema de que el tiempo que le pondo en el timeout es el que me toma y no me refresca la sesio, o sea que si le pongo timeout=”30″ a los 30 min me bota no importa que este trabajando de forma continua, estuve probando con la solucion que planteaste pero me sale error a la hor que sube el script al encabezado, tengo el slidingExpiration=”true” asi que no me explico por que me saca la sesio, mas bien como que caducan mis variables de sesion y pierdo los valores, agradezco cualquier sugerencia, gracias

  19. intente con un update panel y un timer para que hiciera un postback asincrono, pero aun asi me caduca la sesion, mis variables de sesion se mueren 🙁

  20. Hola
    He probado el codigo de ejemplo siendo la pagina VB yno hay problema, pero mi proyecto las apginas son C#.
    Cuando meto el c´digo Script del interval final, me da un erro no reconociendo Session.Timeout como sigue
    Mensaje de error del compilador: CS1061: ‘System.Web.SessionState.HttpSessionState’ no contiene una definición de ‘TimeOut’ ni se encontró ningún método de extensión ‘TimeOut’ que acepte un primer argumento de tipo ‘System.Web.SessionState.HttpSessionState’ (¿falta una directiva using o una referencia de ensamblado?)

    Error de código fuente:

    Línea 30:
    Línea 31: //Temporizador para matener la sesión activa
    Línea 32: setInterval(“MantenSesion()”, < %= 0.9*( Session.TimeOut*60000) %>);
    Línea 33:
    Línea 34: //–>

  21. Hola Emilio:

    El código es exactamente el mismo en VB y en C#.
    El error tiene toda la pinta de que, efectivamente, te falte una referencia, porque de otro modo no se explica.
    ¿Has visto el vídeo práctico? Ahí te puedes bajar el código también.

    Saludos

    JM

  22. Emilio, Si estas programando con C#; Para el error de:

    setInterval(“MantenSesion()”, < %= 0.9*( Session.TimeOut*60000) %>);

    por que no pruebas poniendo la palabra TimeOut de la siguiente manera Timeout a mi me dejo de marcar el error o como dice José M. la referencia sería System.Web.SessionState.

    Pero aun no logro mantener la sesión no se si es porque utilizo un “UpdateProgress” y muestro mi pantalla como un ModalPopUp.

    Me pueden ayudar, de todas formas mil gracias.

  23. Hola:

    No es porque tenga sun modal popup porque al final de lo que se trata es de que llegue al servidor alguna cabecera con la cookie de sesión antes de que pase el tiempo máximo. Da igual desde donde se haga esa llamada.

    El UpdateProgress ¿para qué lo usas? Si estás haciendo la llamada como yo muestro en mi código y vídeo no te hace falta para nada.

    Si os bajás el código de ejemplo en el enlace abajo de todo del post no deberías tener problemas de reproducirlo.

    Saludos

    JM

  24. ola tengo un problemia tengo que generar un query y tarda demasiado mas de 2 oras y si se genera el problema es que la pagina die que ya expiro ayuda porfaor

  25. cuando trabajo como local …me funciona……pereo al dubirlo al servidor…es dodne tengo problemas…sigue caducando a los 5 minutos y me redirecciona a login.aspx…ayudaaaaaaaaaaa

  26. Muchas gracias me sirvio de mucha ayuda tu post, lo que me pregunto es ahora, yo uso asp.net 2008 y tengo dos MASTER una para usuario y otra para un administrador, En el MASTER de usuario funciona correctamente, el MASTER de Administrador se encuentra en una carpeta administrador, teniendo el handler.ashx a nivel RAIZ, tengo ke cambiar la ruta del JAvascript del MASTER administrador para que funcione ??????
    en Codigo tengo que poner :
    var CONTROLADOR = “../Handler.ashx”; ???????

  27. Muy buen tip. Tengo el siguiente detalle, en el entorno de desarrollo funciona perfectamente bien, pero en producción las variable de sesión a pesar de que si se habla el manejador genérico (ashx).
    La configuración en producción es la siguiente:
    1. Se tiene un servidor web que aloja dos páginas.
    2. Exista un sitio web ajeno a mi sistema que mediante un enlace invoca a mi sitio web.
    3. El sitio web donde se implementa este tip, es un webapplication

    ¿Tendrás alguna de que puede estar pasando?
    De antemano gracias por este aporte.

  28. Hola José, he utilizado el código de ejemplo y funciona bien, pero tengo un problema cuado se tienen datos capturados en el formulario, se realiza el postback y se pierde la información capturada

  29. Hola,

    Si se tiene implementada la autenticación por formularios, se descuadran los tiempos de timeout de sesión y el de la cookie de autenticación, porque en cada petición, se renueva el tiempo de sesión, pero el de la cookie solo se renueva si lleva más de la mitad del configurado. Entonces, al intentar renovar la sesión llamando a la página .ashx, si ha caducado el tiempo de autentificación, nos devolverá un error.
    Si alguien sabe como solucionar este problema, será de mucha utilidad…

  30. Hola José M. Alarcón Aguín, te cuento que utilice el ejemplo que colocastey me funciono al 100%. Lo que si despues dejo de funcionar cuando agregue un control update panel para usar ajax en mi pagina. Ahi dejo de funcionar el script, y la pagina sigue caducando. Alguna sugerencia??? Por favor nose que mas hacer.

  31. Hola José M. Alarcón Aguín, te cuento que utilice el ejemplo que colocastey me funciono al 100%. Lo que si despues dejo de funcionar cuando agregue un control update panel para usar ajax en mi pagina. Ahi dejo de funcionar el script, y la pagina sigue caducando. Alguna sugerencia??? Por favor nose que mas hacer.

  32. Hola José Manuel,

    Excelente solución, a mi tampoco se me habia ocurrido algo así.

    Sería posible renovar de forma similar la cookie que se genera cuando se usa el proveedor de asp.net para autenticacion mediante formularios?, ya que si un usuario se loguea, con tu procedimiento se mantiene la session, perfecto, pero la cookie de autenticación expira con lo cual aunque la session no ha terminado el usuario es redireccionado a la página de login.

    No se si me explico, seria de gran ayuda algo asi como la sincronización de la session y el timeout de la autenticación.

    Bueno, en espera de una contestación , muchisimas gracias por este gran aporte

    PD: He sido alumno y seguramente volveré a serlo de http://WWW.CAMPUSMVP.COM , totalmente recomendable. Esto ademas de peloteo es verdad.

    Saludos!!!

  33. Hola Jorge:

    Gracias por los cumplidos de campusMVP 🙂

    La cookie de autenticación es otra cosa completamente diferente que se mantiene en el cliente y por lo tanto no se puede seguir una técnica como esta para gestionarla.

    En este post de mi blog:

    http://www.jasoft.org/blog/PermaLink,guid,e7174bfd-93d2-47f8-8a14-9c4db2cffd63.aspx

    comento como funcionan.

    Como las cookies funcionan con cualquier petición HTTP imagino que sería fácil hacer que la cookie de autenticación se renueve en cada petición que se hace al servidor con la técnica que explico arriba (y el vídeo correspondiente si sigues los enlaces).

    Ahora mismo no tengo tiempo de nada, así que no puedo probarlo, pero si lo pruebas sería estupendo que lo comentaras aquí para saber si te ha funcionado.

    Saludos,

    JM

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *