Maintenir la session ASP.NET ouverte / active

116

Quelle est la manière la plus simple et la plus discrète de maintenir une session ASP.NET active tant que la fenêtre du navigateur est ouverte? S'agit-il d'appels AJAX chronométrés? Je veux éviter ce qui suit: parfois les utilisateurs gardent leur fenêtre ouverte pendant une longue période, puis entrent des éléments, et lors de l'envoi, rien ne fonctionne plus car la session côté serveur a expiré. Je ne souhaite pas augmenter la valeur du délai d'attente pendant plus de 10 minutes sur le serveur car je souhaite que les sessions fermées (en fermant la fenêtre du navigateur) expirent rapidement.

Suggestions, exemples de code?

Alex
la source
Vous pouvez également consulter ce lien pour obtenir une réponse dotnetcurry.com/ShowArticle.aspx?ID=453
Développeur

Réponses:

171

J'utilise JQuery pour effectuer un simple appel AJAX à un gestionnaire HTTP factice qui ne fait rien d'autre que maintenir ma session en vie:

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"
    );
}

Le gestionnaire de session peut être aussi simple que:

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

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

La clé est d'ajouter IRequiresSessionState, sinon Session ne sera pas disponible (= null). Le gestionnaire peut bien sûr également renvoyer un objet sérialisé JSON si certaines données doivent être renvoyées au JavaScript appelant.

Mis à disposition via web.config:

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

ajouté de balexandre le 14 août 2012

J'ai tellement aimé cet exemple que je veux m'améliorer avec le HTML / CSS et la partie beat

change ça

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

dans

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

et ajouter

// 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 et CSS

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

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

voici un exemple en direct pour seulement la partie battante: http://jsbin.com/ibagob/1/

Veggerby
la source
@veggerby "à un gestionnaire HTTP factice qui ne fait rien mais maintient ma session en vie". Pouvez-vous s'il vous plaît publier un exemple de code de HTTP Handler pour maintenir la session en vie?
Gopinath
non, et votre seule option (je suppose) est d'augmenter le délai d'expiration de la session, mais c'est probablement une mauvaise idée à long terme
vegger du
A fait des enquêtes sur un problème connexe et est tombé sur cette solution. Bon produit. Une question cependant, si l'utilisateur laisse son navigateur ouvert et dit que le PC ne se met pas en veille pendant 10 heures, la session sera maintenue en vie pendant si longtemps, non? Est-ce correct?
Julius A
2
Beau travail mais cela m'a échoué jusqu'à ce que j'ajoute une rafale de cache à l'appel. Sans ce paramètre de rafale de cache, le contrôleur est appelé juste pour la première fois
jean
1
@stom c'est censé être une valeur de session, pas un timeout ou quoi que ce soit. La raison de l'utilisation DateTime.Nowétait simplement de faire apparaître la dernière mise à jour de la session via le battement de cœur.
veggerby le
69

Si vous utilisez ASP.NET MVC, vous n'avez pas besoin d'un gestionnaire HTTP supplémentaire ni de certaines modifications du fichier web.config. Tout ce dont vous avez besoin - juste pour ajouter une action simple dans un contrôleur Home / Common:

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

, écrivez un morceau de code JavaScript comme celui-ci (je l'ai mis dans l'un des fichiers JavaScript du site):

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();
}

, et initialisez cette fonctionnalité en appelant une fonction JavaScript:

SetupSessionUpdater('/Home/KeepSessionAlive');

Notez s'il vous plaît! J'ai implémenté cette fonctionnalité uniquement pour les utilisateurs autorisés (il n'y a aucune raison de garder l'état de session pour les invités dans la plupart des cas) et la décision de garder l'état de session actif n'est pas seulement basée sur - le navigateur est ouvert ou non, mais l'utilisateur autorisé doit faire une certaine activité sur le site (déplacez une souris ou tapez une touche).

Maryan
la source
3
Pour MVC, je pense que c'est la meilleure réponse. Pas besoin d'utiliser le fichier .ashx, pourquoi le feriez-vous?
arame3333
Avec HTTP Handler dans asp mvc, vérifiez ceci , l'espoir aide quelqu'un.
shaijut
3
Maryan, vous pouvez également utiliser @Url.Action("KeepSessionAlive","Home")la fonction d'initialisation pour ne pas avoir à coder en dur l'URL et également lancer le premier bloc à l'intérieur d'un IIFE et simplement exporter le SetupSessionUpdatercar c'est la seule chose qui doit être invoquée en externe - quelque chose comme ceci: SessionUpdater.js
KyleMit
Y a-t-il une raison pour laquelle setInterval n'est pas utilisé? setInterval(KeepSessionAlive, 300000)
Matthieu Cormier
8

Chaque fois que vous faites une demande au serveur, le délai d'expiration de la session est réinitialisé. Vous pouvez donc simplement faire un appel ajax à un gestionnaire HTTP vide sur le serveur, mais assurez-vous que le cache du gestionnaire est désactivé, sinon le navigateur mettra en cache votre gestionnaire et ne fera pas de nouvelle requête.

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 - Il n'y a pas besoin de la surcharge de stockage des variables dans la session. Il suffit de préformer une requête auprès du serveur.

BornToCode
la source
2

Avez-vous vraiment besoin de conserver la session (avez-vous des données dedans?) Ou suffit-il de simuler cela en réinstituant la session lorsqu'une demande arrive? Si c'est le premier, utilisez la méthode ci-dessus. Si c'est le cas, essayez quelque chose comme l'utilisation du gestionnaire d'événements Session_End.

Si vous avez une authentification par formulaire, vous obtenez quelque chose dans le Global.asax.cs comme

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);
    ...
     }

Et vous définissez la durée de vie du ticket beaucoup plus longue que la durée de vie de la session. Si vous ne vous authentifiez pas ou si vous utilisez une méthode d'authentification différente, il existe des astuces similaires. L'interface Web de Microsoft TFS et SharePoint semblent les utiliser - le problème est que si vous cliquez sur un lien sur une page obsolète, vous obtenez des invites d'authentification dans la fenêtre contextuelle, mais si vous utilisez simplement une commande, cela fonctionne.

Henry Troup
la source
2

vous pouvez simplement écrire ce code dans votre fichier de script java c'est tout.

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

Le (20-1)*60*1000temps d'actualisation est, il actualisera le délai d'expiration de la session. Le délai de rafraîchissement est calculé comme le délai par défaut de iis = 20 minutes, ce qui signifie que 20 × 60000 = 1200000 millisecondes - 60000 millisecondes (une minute avant l'expiration de la session) est 1140000.

ANKIT KISHOR
la source
0

Voici une solution alternative qui devrait survivre si le PC client passe en mode veille.

Si vous avez un grand nombre d'utilisateurs connectés, utilisez-le avec prudence car cela pourrait consommer beaucoup de mémoire serveur.

Après vous être connecté (je le fais dans l'événement LoggedIn du contrôle de connexion)

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)
Peter
la source
Pourquoi cette décision a-t-elle été rejetée? y a-t-il un problème que je n'ai pas mentionné? si tel est le cas, partagez-le pour que les futurs lecteurs en soient conscients!
Peter
0

J'ai passé quelques jours à essayer de comprendre comment prolonger une session utilisateur dans WebForms via une boîte de dialogue contextuelle donnant à l'utilisateur la possibilité de renouveler la session ou de lui permettre d'expirer. La chose n ° 1 que vous devez savoir est que vous n'avez besoin d'aucun de ces trucs sophistiqués de 'HttpContext' dans certaines des autres réponses. Tout ce dont vous avez besoin est le $ .post () de jQuery; méthode. Par exemple, lors du débogage, j'ai utilisé:

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

et sur votre site en direct, vous utiliseriez quelque chose comme:

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

C'est aussi simple que ça. De plus, si vous souhaitez inviter l'utilisateur à renouveler sa session, procédez comme suit:

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

N'oubliez pas d'ajouter la boîte de dialogue à votre 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
la source
0

En ce qui concerne la solution de veggerby, si vous essayez de l'implémenter sur une application VB, faites attention en essayant d'exécuter le code fourni via un traducteur. Ce qui suit fonctionnera:

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

Aussi, au lieu d'appeler comme heartbeat () fonction comme:

 setTimeout("heartbeat()", 300000);

Au lieu de cela, appelez-le comme:

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

Numéro un, setTimeout ne se déclenche qu'une seule fois alors que setInterval se déclenche à plusieurs reprises. Numéro deux, appeler heartbeat () comme une chaîne ne fonctionnait pas pour moi, alors que l'appeler comme une fonction réelle le faisait.

Et je peux absolument confirmer à 100% que cette solution surmontera la décision ridicule de GoDaddy de forcer une session Apppool de 5 minutes à Plesk!

AfficherJX1990
la source
0

Voici la version du plugin JQuery de la solution Maryan avec optimisation des poignées. Uniquement avec 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)

et comment il importe dans votre * .cshtml

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

[En retard à la fête ...]

Une autre façon de faire cela sans la surcharge d'un appel Ajax ou d'un gestionnaire WebService consiste à charger une page ASPX spéciale après un laps de temps donné (c'est-à-dire avant l'expiration de l'état de session, qui est généralement de 20 minutes):

// 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.aspxpage est simplement une page vide qui ne fait que toucher / rafraîchir l' Sessionétat:

// 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;
    }
}

Cela fonctionne en créant un imgélément (image) et en forçant le navigateur à charger son contenu à partir de la KeepAlive.aspxpage. Le chargement de cette page amène le serveur à toucher (mettre à jour) l' Sessionobjet, prolongeant la fenêtre de temps glissant d'expiration de la session (généralement de 20 minutes supplémentaires). Le contenu réel de la page Web est supprimé par le navigateur.

Une autre façon, et peut-être plus propre, de le faire est de créer un nouvel iframeélément et d'y charger la KeepAlive.aspxpage. L' iframeélément est masqué, par exemple en en faisant un élément enfant d'un divélément masqué quelque part sur la page.

L'activité sur la page elle-même peut être détectée en interceptant les actions de la souris et du clavier pour tout le corps de la page:

// 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);
}

Je ne peux pas m'attribuer le mérite de cette idée; voir: https://www.codeproject.com/Articles/227382/Alert-Session-Time-out-in-ASP-Net .

David R Tribble
la source