JASoft.org

El blog de José Manuel Alarcón Aguín. Programación web y mucho más...

MENÚ - JASoft: JM Alarcón

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!

 

José Manuel Alarcón José Manuel Alarcón
Fundador de campusMVP.es, el proyecto de referencia en formación on-line para programadores en lengua española. Autor de varios libros y cientos de artículos. Galardonado como MVP de Microsoft desde 2004. Gallego de Vigo, amante de la ciencia y la tecnología, la música y la lectura. Ayudando a la gente en Internet desde 1996.
Descarga GRATIS mi último libro (no técnico): "Tres Monos, Diez Minutos".
Banner

Comentarios (31) -

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

Responder

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

Responder

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

Responder

Asier:

Si utilizas el Timer, el cual hace una llamada asíncrona al servidor, no es necesario que prorogues la sesión en el evento Tick ya que la propia llamada recibida en el servidor la va a prolongar automaticamente.

Saludos

JM

Responder

Hola ALM:

Algo debes de tener mal escrito porque eso funciona sin problemas. Tu página ASPX ¿enqué lenguaje d eprogramación está? Ten en cuenta que esa expresión es en C#, y si tu página es deVB no te va a compilar.

A ver si va a ser eso...

Saludos

JM

Responder

Hola ALM:

Sería así:

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

Sólo cambia la sintaxis dentro de <% %> que es la parte escrita en .NET (el resto es JavaScript).

Saludos

JM

Responder

Hola de nuevo:

Bueno, en realidad no es necesario (ni recomendable) que vaya fuera del body. cuando digo al final de la página me refiero al final de los elementos de la página, asíq ue justo antes del cierre del body está perfecto.

Si te caduca la sesión puede que no estés llamando a la página apropiada o que tengas algo mal por ahí.

Te recomiendo que le eches un vistazo a los comentarios que ha hehco la gente en el Crossposting de este artículo en Geeks.ms, pues han salido muchas situaciones también:

geeks.ms/.../88684.aspx">geeks.ms/.../88684.aspx

Al ritmo de problemas que está dando este post voy a intentar generar un proyecto de ejemplo y ponerlo por aquí para darlo todo hecho. A ver si logro sacar tiempo un día de estos.

Saludos

JM

Responder

saludos, pues aun no encuentro como hacer que funciones como lo planteas tu JM, te agradezco el post, aunq seria mejor un ejemplillo po hay, por cierto, yo uso un frame para mis paginas y la default como principal, no se si sea poe eso que no logre la persistencia de mis sesiones, gracias

Responder

Ok ese ejemplo esta de pelos, jeje super al finm entendi por que me marcara error al querer implementar la persistencia de la sesion, gracias Juan y saludos

Responder

disculpa una pregunta, en un ambiente donde tengo una ventana principal la cual contiene un frame y dentro del frame navega el usuario, en que parte de la aplicacion deberia poner el script para q no caduque la sesion?? en el principal? o en las q van a aparecer en el frame?? o en ambas??? gracias

Responder

Hola Karlo:

Da igual en cual de los marcos o iframes lo pongas, llega con que se haga una llamada al servidor de vez en cuando independientemente de donde esté ubicada en el cliente, mientras esté dentro de la misma sesión del navegador (www.jasoft.org/.../...-4dde-a607-1a8ec1067e6b.aspx)

Saludos

JM

Responder

saludos una pregunta con respecto a la anterior, si la pagina se encuentra con frames, lo de las directivas P3P tendran algo q ver con q se borran las vaiable d esesion?? gracias

Responder

No lo creo Karlo. Dudo que eso de P3P tenga algo que ver con la sesión, pero lo desconozco.

Saludos

JM

Responder

saludos nuevamente, mira todo fnciona de maravilla, a menos que mande un alert o un confirm y el usuario lo deje abierto despues de un tiempo, regresa, y o desilucion su sesion expiro, alguna sugerenca para evitar esto?? gracias, y es que las ventanitas esas como ayudan, pero en este caso como molestan, gracias

Responder

Hola Karlo:

Las ventanas de alerta y confirmación de JavaScritp son modales, es decirm, bloquean la ejecución. Por ello mucho me temo que no puedes hacer nada excepto sustituirlas por otra cosa.

En cualquier caso en la actualidad las ventanas de alerta de JavaScript se consideran un anacronismo y se da preferencia a otras formas de aviso, como advertencias con colores (como en Facebook) o ventanas simuladas hechas con capas.

Si cambias a este tipo de advertencias verás que no tienes problema.

Saludos

JM

Responder

saludos. como se puede implementar esto en asp.

Responder

Exactamente igual pero sustituyendo el .ashx por cualquier .asp. Es muy fácil.

Saludos

Responder

Ok, hare las pruebas

Gracias

Responder

Hola José

Es necesario q la funcion MantenSesion() se encuentre en cada página q contenga una sesion o da igual q haga una llamada a un archivo que contenga esta funcion.

<script language="javascript" src="../funciones.js"></script>

Gracias.

Sonia

Responder

Hola Sonia:

Da igual que la pongas en todas o que la incluyas en un .js externo para añadirla a las páginas en las que quieras mantener la sesión, es equivalente (si he entendido bien tu pregunta)

Saludos

JM

Responder

saludos, pues he estado haciendo mis pruebas y el detalle es cuando implemento la autenticacion por formularios, al autenticar al usuario y despues pasa el tiempo y la sesion expira, algun consejo??

Responder

Hola Jose gracias por el aporte, te comento que lo puse en practica y me funciono a las mil maravillas :D pero quiero saber como hago para que no me aparezca como advertencia esos simbolos en el javascript "<%" aunque lo que contiene es codigo asp.net. Y otra duda que tengo es referente a las advertencias y mensajes que muestra en visual studio cuando compila su aplicacion que significa que si no me muestra ningun mensaje o advertencia mi aplicacion esta bien y sino esta mal?

Saludos

Responder

que tal amigo, mira yo lo puse en una master page y genera una excepción, pero lo probé en una pagina normal y si funciona bien, porque crees que en la master page no funcione?

Responder

Peru carlos_mmq

hola, tu explicacion es cierta, pero todavia tengo un problema, el tiempo de mi sesion por asi decirlo no expira, pero leugo de un tiempo prolongado, me bota el error : server error in '/...' Aplication.
como puedo hacer q despues de un tiempo prolongado pueda seguir ingresando datos para la busqueda de mi web...
espero q puedan ayudarme...
gracias

Responder

Hola, alguien lo ha hecho pero en php?? tengo el mismo problema...solo que no tengo acceso al php.ini, de que otra manera puedo hacerlo? alguien sabe.?

Muchas gracias de antemano.

Responder

Hola Jose

Como haria para implementarlo en una Master Page.

Responder

Has podido solucionar el problema en la Master Page para mantener la session activa?

Responder

Bolivia RICARDO ARANIBAR

Como seria la  de su propuesta pero en ASP.Net MVC4?

Responder

Podría aplicar igual para ASP.NET MVC 5?
Otra cuestión es la cookie de autenticación que tiene su timeout.

Responder

by Jose M. Alarcon

Hola:

Mientras la petición que realices sea procesada por el framework que hay debajo, te sirve para cualquier lenguaje/framework de servidor. Quiero decir: si llamas a un recurso estático y éste no es gestionado antes por el pipeline de tu tecnología, entonces la cookie no se refrescará. Por lo tanto en PHP o ASP Clásico deberías hacer una llamada a una página .php o .asp respectivamente, pero frameworks como el que mencionas (ASP.NET MVC) o incluso en ASP.NET "tradicional" si el modo de gestión de peticiones de IIS es el Integrado (en el que todas las peticiones van a través de su pipeline) puedes llamar a cualquier recurso.

Saludos.

Responder

SessionState timeout y authentication timeout con el mismo valor. Pero hay temas que influyen como el reciclaje del AppPool.

Sesion: In Proc
Modo: Usar cookies
ASP.NET_SessionId
Tiempo de espera: 2040 minutos
En el IIS 8.5, En las propiedades de la aplicación no vi nada al respecto de timeout.

En appPool, qué valores das a  shutdown worker process if idle y recycle worker process ?

En mi AppPool aparece:
Reciclaje
1) Intervalos fijos
Intervalos de tiempo regulares (en minutos): 1740
Why is the IIS default app pool recycle set to 1740 minutes?
weblogs.asp.net/.../why-is-the-iis-default-app-pool-recycle-set-to-1740-minutes

En Process Model:

"Process Model" section

→ "Idle Timeout Action" : TErminate, Suspend
→ "Idle Timeout" (default 20 minutes)
→ Identity: ApplicationPoolIdentity
→ ShutdownTimeLimit: (default 90 seconds)
→ StartupTimeLimit: (default 90 seconds)


    Under the "Recycle" section → "Regular Time Interval" (default 1740 minutes)

Idle Time-out Action : Suspend setting
Suspending is just freezes the process and it is much more efficient than the destroying the process.
nimitsharma.wordpress.com/.../
www.coreblox.com/.../iis7-application-pool-recycling-and-idle-time-out

You need to set your application pool startMode to be AlwaysRunning:
www.red-gate.com/.../

Responder

Agregar comentario