Mantener la sesión ASP.NET abierta / activa

115

¿Cuál es la forma más fácil y discreta de mantener viva una sesión ASP.NET siempre que el usuario tenga la ventana del navegador abierta? ¿Son llamadas AJAX cronometradas? Quiero evitar lo siguiente: a veces los usuarios mantienen su ventana abierta durante mucho tiempo, luego ingresan cosas y, al enviar, ya no funciona nada porque la sesión del lado del servidor expiró. No quiero aumentar el valor de tiempo de espera durante más de 10 minutos en el servidor, ya que quiero que las sesiones cerradas (cerrando la ventana del navegador) se agoten rápidamente.

¿Sugerencias, ejemplos de código?

Alex
fuente
También puede consultar este enlace para obtener la respuesta dotnetcurry.com/ShowArticle.aspx?ID=453
Desarrollador

Respuestas:

170

Utilizo JQuery para realizar una simple llamada AJAX a un controlador HTTP ficticio que no hace más que mantener viva mi sesión:

function setHeartbeat() {
    setTimeout("heartbeat()", 5*60*1000); // every 5 min
}

function heartbeat() {
    $.get(
        "/SessionHeartbeat.ashx",
        null,
        function(data) {
            //$("#heartbeat").show().fadeOut(1000); // just a little "red flash" in the corner :)
            setHeartbeat();
        },
        "json"
    );
}

El controlador de sesión puede ser tan simple como:

public class SessionHeartbeatHttpHandler : IHttpHandler, IRequiresSessionState
{
    public bool IsReusable { get { return false; } }

    public void ProcessRequest(HttpContext context)
    {
        context.Session["Heartbeat"] = DateTime.Now;
    }
}

La clave es agregar IRequiresSessionState; de ​​lo contrario, la sesión no estará disponible (= nulo). Por supuesto, el controlador también puede devolver un objeto serializado JSON si algunos datos deben devolverse al JavaScript de llamada.

Disponible a través de web.config:

<httpHandlers>
    <add verb="GET,HEAD" path="SessionHeartbeat.ashx" validate="false" type="SessionHeartbeatHttpHandler"/>
</httpHandlers>

añadido de balexandre el 14 de agosto de 2012

Me gustó tanto este ejemplo, que quiero mejorar con HTML / CSS y la parte de ritmo

cambia esto

//$("#heartbeat").show().fadeOut(1000); // just a little "red flash" in the corner :)

dentro

beatHeart(2); // just a little "red flash" in the corner :)

y añadir

// beat the heart 
// 'times' (int): nr of times to beat
function beatHeart(times) {
    var interval = setInterval(function () {
        $(".heartbeat").fadeIn(500, function () {
            $(".heartbeat").fadeOut(500);
        });
    }, 1000); // beat every second

    // after n times, let's clear the interval (adding 100ms of safe gap)
    setTimeout(function () { clearInterval(interval); }, (1000 * times) + 100);
}

HTML y CSS

<div class="heartbeat">&hearts;</div>

/* HEARBEAT */
.heartbeat {
    position: absolute;
    display: none;
    margin: 5px;
    color: red;
    right: 0;
    top: 0;
}

aquí hay un ejemplo en vivo solo para la parte de la paliza: http://jsbin.com/ibagob/1/

veggerby
fuente
@veggerby "a un controlador HTTP ficticio que no hace más que mantener viva mi sesión". ¿Puede publicar un código de muestra del controlador HTTP para mantener viva la sesión?
Gopinath
no, y su única opción allí (supongo) es aumentar el tiempo de espera de la sesión, pero probablemente sea una mala idea a largo plazo
vegger por
Estaba haciendo investigaciones sobre un tema relacionado y encontré esta solución. Buen material. Sin embargo, una consulta, si el usuario deja su navegador abierto y dice que la PC no se duerme durante 10 horas, la sesión se mantendrá viva durante tanto tiempo, ¿verdad? ¿Es esto correcto?
Julio A
2
Buen trabajo, pero me falló hasta que agregué una ráfaga de caché a la llamada. Sin este parámetro de ráfaga de caché, el controlador se llama solo la primera vez
jean
1
@stom está destinado a ser un valor de sesión, no un tiempo de espera ni nada. La razón para usarlo DateTime.Nowfue solo para hacer evidente cuándo se actualizó la sesión por última vez a través del latido.
vegger por
69

Si está utilizando ASP.NET MVC, no necesita un controlador HTTP adicional ni algunas modificaciones del archivo web.config. Todo lo que necesita, solo para agregar una acción simple en un controlador Inicio / Común:

[HttpPost]
public JsonResult KeepSessionAlive() {
    return new JsonResult {Data = "Success"};
}

, escriba un fragmento de código JavaScript como este (lo he puesto en uno de los archivos JavaScript del sitio):

var keepSessionAlive = false;
var keepSessionAliveUrl = null;

function SetupSessionUpdater(actionUrl) {
    keepSessionAliveUrl = actionUrl;
    var container = $("#body");
    container.mousemove(function () { keepSessionAlive = true; });
    container.keydown(function () { keepSessionAlive = true; });
    CheckToKeepSessionAlive();
}

function CheckToKeepSessionAlive() {
    setTimeout("KeepSessionAlive()", 5*60*1000);
}

function KeepSessionAlive() {
    if (keepSessionAlive && keepSessionAliveUrl != null) {
        $.ajax({
            type: "POST",
            url: keepSessionAliveUrl,
            success: function () { keepSessionAlive = false; }
        });
    }
    CheckToKeepSessionAlive();
}

e inicialice esta funcionalidad llamando a una función de JavaScript:

SetupSessionUpdater('/Home/KeepSessionAlive');

¡Tenga en cuenta! He implementado esta funcionalidad solo para usuarios autorizados (no hay razón para mantener el estado de la sesión para los invitados en la mayoría de los casos) y la decisión de mantener el estado de la sesión activo no solo se basa en si el navegador está abierto o no, pero el usuario autorizado debe realizar alguna actividad en el sitio (mueva el mouse o escriba alguna tecla).

Maryan
fuente
3
Para MVC, creo que esta es la mejor respuesta. No es necesario utilizar el archivo .ashx, ¿por qué lo haría?
arame3333
Con HTTP Handler en asp mvc, verifique esto , espero que ayude a alguien.
shaijut
3
Maryan, es posible que también desee usar @Url.Action("KeepSessionAlive","Home")en la función de inicialización para no tener que codificar la URL y también lanzar el primer bloque dentro de un IIFE y simplemente exportarlo, SetupSessionUpdaterya que eso es lo único que debe invocarse externamente, algo como esto: SessionUpdater.js
KyleMit
¿Hay alguna razón por la que no se utiliza setInterval? setInterval(KeepSessionAlive, 300000)
Matthieu Cormier
8

Cada vez que realiza una solicitud al servidor, se restablece el tiempo de espera de la sesión. Por lo tanto, puede realizar una llamada ajax a un controlador HTTP vacío en el servidor, pero asegúrese de que la caché del controlador esté deshabilitada; de lo contrario, el navegador almacenará en caché su controlador y no realizará una nueva solicitud.

KeepSessionAlive.ashx.cs

public class KeepSessionAlive : IHttpHandler, IRequiresSessionState
    {

        public void ProcessRequest(HttpContext context)
        {
            context.Response.Cache.SetCacheability(HttpCacheability.NoCache);
            context.Response.Cache.SetExpires(DateTime.UtcNow.AddMinutes(-1));
            context.Response.Cache.SetNoStore();
            context.Response.Cache.SetNoServerCaching();
        }
    }

.JS:

window.onload = function () {
        setInterval("KeepSessionAlive()", 60000)
}

 function KeepSessionAlive() {
 url = "/KeepSessionAlive.ashx?";
        var xmlHttp = new XMLHttpRequest();
        xmlHttp.open("GET", url, true);
        xmlHttp.send();
        }

@veggerby: no hay necesidad de la sobrecarga de almacenar variables en la sesión. Basta con realizar una solicitud al servidor.

BornToCode
fuente
2

¿Realmente necesita mantener la sesión (¿tiene datos en ella?) O es suficiente falsificar esto reinstalando la sesión cuando entra una solicitud? Si es el primero, utilice el método anterior. Si es el segundo, intente algo como usar el controlador de eventos Session_End.

Si tiene autenticación de formularios, obtendrá algo en Global.asax.cs como

FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(formsCookie.Value);
if (ticket.Expired)
{
    Request.Cookies.Remove(FormsAuthentication.FormsCookieName);
    FormsAuthentication.SignOut();
    ...             
     }
else
{   ...
    // renew ticket if old
    ticket = FormsAuthentication.RenewTicketIfOld(ticket);
    ...
     }

Y establece la duración del ticket mucho más larga que la duración de la sesión. Si no se está autenticando o está utilizando un método de autenticación diferente, existen trucos similares. La interfaz web de Microsoft TFS y SharePoint parecen usarlos; el regalo es que si hace clic en un enlace en una página obsoleta, obtiene mensajes de autenticación en la ventana emergente, pero si solo usa un comando, funciona.

Henry Troup
fuente
2

simplemente puede escribir este código en su archivo de script java, eso es todo.

$(document).ready(function () {
        var delay = (20-1)*60*1000;
        window.setInterval(function () {
            var url = 'put the url of some Dummy page';
            $.get(url);                
        }, delay);
});

El (20-1)*60*1000es tiempo de actualización, se actualizará el tiempo de espera de sesión. El tiempo de espera de actualización se calcula como el tiempo predeterminado fuera de iis = 20 minutos, lo que significa que 20 × 60000 = 1200000 milisegundos - 60000 milisegundos (un minuto antes de que expire la sesión) es 1140000.

ANKIT KISHOR
fuente
0

Aquí hay una solución alternativa que debería sobrevivir si la PC del cliente entra en modo de suspensión.

Si tiene una gran cantidad de usuarios registrados, utilícelo con precaución, ya que podría consumir mucha memoria del servidor.

Después de iniciar sesión (hago esto en el evento LoggedIn del control de inicio de sesión)

Dim loggedOutAfterInactivity As Integer = 999 'Minutes

'Keep the session alive as long as the authentication cookie.
Session.Timeout = loggedOutAfterInactivity

'Get the authenticationTicket, decrypt and change timeout and create a new one.
Dim formsAuthenticationTicketCookie As HttpCookie = _
        Response.Cookies(FormsAuthentication.FormsCookieName)

Dim ticket As FormsAuthenticationTicket = _
        FormsAuthentication.Decrypt(formsAuthenticationTicketCookie.Value)
Dim newTicket As New FormsAuthenticationTicket(
        ticket.Version, ticket.Name, ticket.IssueDate, 
        ticket.IssueDate.AddMinutes(loggedOutAfterInactivity), 
        ticket.IsPersistent, ticket.UserData)
formsAuthenticationTicketCookie.Value = FormsAuthentication.Encrypt(newTicket)
Pedro
fuente
¿Por qué se votó en contra? ¿Hay algún tipo de problema que no haya mencionado? si ese es el caso, por favor compártelo para que los futuros lectores lo sepan.
Peter
0

Pasé unos días tratando de averiguar cómo prolongar la sesión de un usuario en WebForms a través de un cuadro de diálogo emergente que le da al usuario la opción de renovar la sesión o permitir que caduque. Lo # 1 que debe saber es que no necesita nada de este elegante 'HttpContext' en algunas de las otras respuestas. Todo lo que necesita es $ .post () de jQuery; método. Por ejemplo, mientras depuraba usé:

$.post("http://localhost:5562/Members/Location/Default.aspx");

y en su sitio en vivo usaría algo como:

$.post("http://mysite/Members/Location/Default.aspx");

Es tan fácil como eso. Además, si desea solicitar al usuario la opción de renovar su sesión, haga algo similar a lo siguiente:

    <script type="text/javascript">
    $(function () { 
        var t = 9;
        var prolongBool = false;
        var originURL = document.location.origin;
        var expireTime = <%= FormsAuthentication.Timeout.TotalMinutes %>;

        // Dialog Counter
        var dialogCounter = function() {
            setTimeout( function() {
                $('#tickVar').text(t);
                    t--;
                    if(t <= 0 && prolongBool == false) {
                        var originURL = document.location.origin;
                        window.location.replace(originURL + "/timeout.aspx");
                        return;
                    }
                    else if(t <= 0) {
                        return;
                    }
                    dialogCounter();
            }, 1000);
        }

        var refreshDialogTimer = function() {
            setTimeout(function() { 
                $('#timeoutDialog').dialog('open');
            }, (expireTime * 1000 * 60 - (10 * 1000)) );
        };

        refreshDialogTimer();

        $('#timeoutDialog').dialog({
            title: "Session Expiring!",
            autoOpen: false,
            height: 170,
            width: 350,
            modal: true,
            buttons: {
                'Yes': function () {
                    prolongBool = true;
                    $.post("http://localhost:5562/Members/Location/Default.aspx"); 
                    refreshDialogTimer();
                    $(this).dialog("close");
                },
                Cancel: function () {
                    var originURL = document.location.origin;
                    window.location.replace(originURL + "/timeout.aspx");
                }
            },
            open: function() {
                prolongBool = false;
                $('#tickVar').text(10);
                t = 9;
                dialogCounter();
            }
        }); // end timeoutDialog
    }); //End page load
</script>

No olvide agregar el cuadro de diálogo a su html:

        <div id="timeoutDialog" class='modal'>
            <form>
                <fieldset>
                    <label for="timeoutDialog">Your session will expire in</label>
                    <label for="timeoutDialog" id="tickVar">10</label>
                    <label for="timeoutDialog">seconds, would you like to renew your session?</label>
                </fieldset>
            </form>
        </div>
Colin
fuente
0

Con respecto a la solución de veggerby, si está intentando implementarla en una aplicación VB, tenga cuidado al intentar ejecutar el código suministrado a través de un traductor. Lo siguiente funcionará:

Imports System.Web
Imports System.Web.Services
Imports System.Web.SessionState

Public Class SessionHeartbeatHttpHandler
    Implements IHttpHandler
    Implements IRequiresSessionState

    ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
        Get
            Return False
        End Get
    End Property

    Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
        context.Session("Heartbeat") = DateTime.Now
    End Sub
End Class

Además, en lugar de llamar como heartbeat (), funciona como:

 setTimeout("heartbeat()", 300000);

En cambio, llámalo como:

 setInterval(function () { heartbeat(); }, 300000);

Número uno, setTimeout solo se dispara una vez, mientras que setInterval se dispara repetidamente. Número dos, llamar a heartbeat () como una cadena no me funcionó, mientras que llamarlo como lo hizo una función real.

Y puedo confirmar absolutamente al 100% que esta solución superará la ridícula decisión de GoDaddy de forzar una sesión de grupo de aplicaciones de 5 minutos en Plesk.

ShowJX1990
fuente
0

Aquí la versión del complemento JQuery de la solución Maryan con optimización de manejo. ¡Solo con JQuery 1.7+!

(function ($) {
    $.fn.heartbeat = function (options) {
        var settings = $.extend({
            // These are the defaults.
            events: 'mousemove keydown'
            , url: '/Home/KeepSessionAlive'
            , every: 5*60*1000
        }, options);

        var keepSessionAlive = false
         , $container = $(this)
         , handler = function () {
             keepSessionAlive = true;
             $container.off(settings.events, handler)
         }, reset = function () {
             keepSessionAlive = false;
             $container.on(settings.events, handler);
             setTimeout(sessionAlive, settings.every);
         }, sessionAlive = function () {
             keepSessionAlive && $.ajax({
                 type: "POST"
                 , url: settings.url
                 ,success: reset
                });
         };
        reset();

        return this;
    }
})(jQuery)

y cómo se importa en su * .cshtml

$('body').heartbeat(); // Simple
$('body').heartbeat({url:'@Url.Action("Home", "heartbeat")'}); // different url
$('body').heartbeat({every:6*60*1000}); // different timeout
AlexPalla
fuente
0

[Tarde a la fiesta ...]

Otra forma de hacer esto sin la sobrecarga de una llamada Ajax o un controlador WebService es cargar una página ASPX especial después de un período de tiempo determinado (es decir, antes del tiempo de espera del estado de la sesión, que suele ser de 20 minutos):

// Client-side JavaScript
function pingServer() {
    // Force the loading of a keep-alive ASPX page
    var img = new Image(1, 1);
    img.src = '/KeepAlive.aspx';
}

La KeepAlive.aspxpágina es simplemente una página vacía que no hace más que tocar / actualizar el Sessionestado:

// KeepAlive.aspx.cs
public partial class KeepSessionAlive: System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        // Refresh the current user session
        Session["refreshTime"] = DateTime.UtcNow;
    }
}

Esto funciona creando un imgelemento (imagen) y obligando al navegador a cargar su contenido desde la KeepAlive.aspxpágina. La carga de esa página hace que el servidor toque (actualice) el Sessionobjeto, extendiendo la ventana de tiempo deslizante de expiración de la sesión (generalmente otros 20 minutos). El navegador descarta el contenido real de la página web.

Una forma alternativa, y quizás más limpia, de hacer esto es crear un nuevo iframeelemento y cargar la KeepAlive.aspxpágina en él. El iframeelemento está oculto, por ejemplo, convirtiéndolo en un elemento secundario de un divelemento oculto en algún lugar de la página.

La actividad en la propia página se puede detectar interceptando las acciones del mouse y del teclado para todo el cuerpo de la página:

// Called when activity is detected
function activityDetected(evt) {
    ...
}

// Watch for mouse or keyboard activity
function watchForActivity() {
    var opts = { passive: true };
    document.body.addEventListener('mousemove', activityDetected, opts);
    document.body.addEventListener('keydown', activityDetected, opts);
}

No puedo atribuirme el mérito de esta idea; ver: https://www.codeproject.com/Articles/227382/Alert-Session-Time-out-in-ASP-Net .

David R Tribble
fuente