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.

Archivado en:
Comparte este post:

Comentarios

# Juan Quijano said:

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.

Thursday, June 12, 2008 12:43 PM
# José M. Alarcón Aguín said:

Juan:

Me alegro de que te resulte útil :-)

Thursday, June 12, 2008 7:15 PM
# Iván said:

Hola

¿Podrías decirnos cúal es el contenido del archivo *.ashx?

Gracias

Monday, October 13, 2008 12:34 PM
# José M. Alarcón Aguín said:

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

Monday, October 13, 2008 1:38 PM
# Roberto said:

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.

Tuesday, October 21, 2008 11:44 AM
# José M. Alarcón Aguín said:

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

Tuesday, October 21, 2008 4:43 PM
# Roberto said:

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.

Saturday, October 25, 2008 11:38 PM
# José M. Alarcón Aguín said:

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

Sunday, October 26, 2008 11:15 AM
# Roberto said:

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.

Sunday, October 26, 2008 11:44 AM
# José M. Alarcón Aguín said:

Hola Roberto:

Sí que doy clases pero virtuales asíq ue no importa que estés en Madrid o donde sea.

Échale un vistazo a esto:

http://www.campusmvp.com

Te recomiendo especialmente estos cursos:

shop.campusmvp.com/Catalog-Cursos-de-Desarrollo-Web_30.aspx

de los que soy tutor en su mayor parte.

Saludos y gracias por la confianza :-)

JM

Sunday, October 26, 2008 12:17 PM
# MArcelo said:

Me parece muy bueno el articulo y sobre todo util, pero una pregunta se puede hacer en VS2003?? Ya que ahi no ahi .ashx ??

Gracias de Antemando.

Wednesday, November 05, 2008 10:05 PM
# Eduardo said:

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

<script language="javascript" type="text/javascript">

   setInterval('MantenSesion()', <%= (int) (0.9 * (Session.Timeout * 60000)) %>);

</script>

me reclama por "<%"

y

me reclama por "Session.Timeout"

espero me puedena ayudar

gracias

atte

Eduardo Baeza O.

Wednesday, December 03, 2008 8:09 PM
# José M. Alarcón Aguín said:

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

Wednesday, December 03, 2008 10:53 PM
# Eduardo said:

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 </html>?

Atento a sus comentarios

Gracias

Saludos cordiales

EB

Thursday, December 04, 2008 2:23 PM
# Eduardo said:

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

Thursday, December 04, 2008 3:11 PM
# osman said:

Donde escrib lo de Function MantenerSession()???..... no me aparece es palabra function en c#...??

Friday, December 12, 2008 4:06 PM
# José M. Alarcón Aguín said:

Osman:

Todo este códig es JavaScript así que lo tienes que poner en el lado cliente (en la parte HTML de la página).

Saludos

JM

Friday, December 12, 2008 4:58 PM
# osman said:

ok....gracias..

Friday, December 12, 2008 8:47 PM
# jenny said:

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

Wednesday, December 17, 2008 12:16 AM
# José M. Alarcón Aguín said:

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

Wednesday, December 17, 2008 9:08 AM
# Antonio said:

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

Monday, January 26, 2009 11:48 AM
# José M. Alarcón Aguín said:

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 <script> en el .js, que ya las tiene en la página a la que lo referencias.

Saludos

JM

Monday, January 26, 2009 8:50 PM
# John said:

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.

Sunday, February 22, 2009 2:21 AM
# morokay said:

Hola José. muy util, sos muy copado y pulenta... muchas gracias.

Thursday, March 26, 2009 3:30 PM
# Karlo said:

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

Friday, March 27, 2009 11:23 PM
# Karlo said:

Si pudieras dar el contenido del ashx, por favor

Saturday, March 28, 2009 12:11 AM
# Karlo said:

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

Saturday, March 28, 2009 2:05 AM
# Karlo said:

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 :(

Saturday, March 28, 2009 2:06 AM
# Emilio said:

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:     //-->

Tuesday, July 14, 2009 12:45 PM
# José M. Alarcón Aguín said:

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

Tuesday, July 14, 2009 1:28 PM
# Mohamed Martinez said:

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.

Tuesday, July 14, 2009 5:34 PM
# José M. Alarcón Aguín said:

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

Tuesday, July 14, 2009 5:42 PM
# Serem said:

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

Wednesday, August 26, 2009 6:19 PM
# Michael.2r said:

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

Sunday, September 13, 2009 2:21 PM
# Jose Luis said:

Mi mas sincera enhorabuena

Muchas gracias!

Friday, January 15, 2010 2:44 PM
# Gabriel Gallegos said:

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";  ???????

Thursday, January 21, 2010 3:58 PM
# Alejandro said:

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.

Friday, February 12, 2010 3:59 PM
# Ismael said:

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

Monday, April 12, 2010 11:57 PM
# David said:

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...

Thursday, May 20, 2010 5:53 PM
# tigre985 said:

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.

Wednesday, July 28, 2010 1:16 AM
# tigre said:

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.

Wednesday, July 28, 2010 1:18 AM
# Jorge said:

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 WWW.CAMPUSMVP.COM , totalmente recomendable. Esto ademas de peloteo es verdad.

Saludos!!!

Thursday, September 02, 2010 5:08 PM
# José M. Alarcón Aguín said:

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:

www.jasoft.org/.../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

Thursday, September 02, 2010 6:35 PM