ASP.NET-Sitzung offen / lebendig halten

115

Welches ist der einfachste und unauffälligste Weg, eine ASP.NET-Sitzung am Leben zu erhalten, solange der Benutzer das Browserfenster geöffnet hat? Ist es zeitgesteuerte AJAX-Anrufe? Ich möchte Folgendes verhindern: Manchmal lassen Benutzer ihr Fenster lange offen, geben dann Daten ein und beim Senden funktioniert nichts mehr, da die serverseitige Sitzung abgelaufen ist. Ich möchte den Timeout-Wert nicht länger als 10 Minuten auf dem Server erhöhen, da geschlossene Sitzungen (durch Schließen des Browserfensters) schnell eine Zeitüberschreitung aufweisen sollen.

Vorschläge, Codebeispiele?

Alex
quelle
Sie können diesen Link überprüfen, um auch eine Antwort zu erhalten. Dotnetcurry.com/ShowArticle.aspx?ID=453
Developer

Antworten:

170

Ich verwende JQuery, um einen einfachen AJAX-Aufruf an einen Dummy-HTTP-Handler auszuführen, der nichts anderes tut, als meine Sitzung am Leben zu erhalten:

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

Der Sitzungshandler kann so einfach sein wie:

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

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

Der Schlüssel ist das Hinzufügen von IRequiresSessionState, da sonst die Sitzung nicht verfügbar ist (= null). Der Handler kann natürlich auch ein serialisiertes JSON-Objekt zurückgeben, wenn einige Daten an das aufrufende JavaScript zurückgegeben werden sollen.

Über web.config verfügbar gemacht:

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

hinzugefügt von balexandre am 14. August 2012

Dieses Beispiel hat mir so gut gefallen, dass ich es mit HTML / CSS und dem Beat-Teil verbessern möchte

ändere das

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

in

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

und hinzufügen

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

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

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

Hier ist ein Live-Beispiel nur für den schlagenden Teil: http://jsbin.com/ibagob/1/

vegetarisch
quelle
@veggerby "an einen Dummy-HTTP-Handler, der nichts anderes tut, als meine Sitzung am Leben zu erhalten". Können Sie bitte einen Beispielcode für den HTTP-Handler veröffentlichen, um die Sitzung am Leben zu erhalten?
Gopinath
Nein, und Ihre einzige Option dort (ich denke) ist es, das Sitzungszeitlimit zu erhöhen, aber das ist auf lange Sicht wahrscheinlich eine schlechte Idee
Veggerby
Hat Untersuchungen zu verwandten Themen durchgeführt und ist auf diese Lösung gestoßen. Gutes Zeug. Eine Frage: Wenn der Benutzer seinen Browser offen lässt und sagt, dass der PC 10 Stunden lang nicht in den Ruhezustand wechselt, wird die Sitzung so lange am Leben bleiben, oder? Ist das richtig?
Julius A
2
Gute Arbeit, aber es ist mir nicht gelungen, bis ich dem Anruf einen Cache-Burst hinzugefügt habe. Ohne diesen Cache-Burst-Parameter wird der Controller nur zum ersten Mal aufgerufen
Jean
1
@stom es bedeutet einen Sitzungswert zu sein, nicht ein Timeout oder nichts. Der Grund für die Verwendung DateTime.Nowwar nur, um deutlich zu machen, wann die Sitzung zuletzt über den Heartbeat aktualisiert wurde.
Veggerby
69

Wenn Sie ASP.NET MVC verwenden, benötigen Sie keinen zusätzlichen HTTP-Handler und einige Änderungen an der Datei web.config. Alles, was Sie brauchen - nur um eine einfache Aktion in einem Home / Common-Controller hinzuzufügen:

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

Schreiben Sie einen JavaScript-Code wie diesen (ich habe ihn in eine der JavaScript-Dateien der Site eingefügt):

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

und initialisieren Sie diese Funktionalität durch Aufrufen einer JavaScript-Funktion:

SetupSessionUpdater('/Home/KeepSessionAlive');

Bitte beachten Sie! Ich habe diese Funktionalität nur für autorisierte Benutzer implementiert (in den meisten Fällen gibt es keinen Grund, den Sitzungsstatus für Gäste beizubehalten). Die Entscheidung, den Sitzungsstatus aktiv zu halten, basiert nicht nur auf - ist der Browser geöffnet oder nicht, sondern der autorisierte Benutzer muss einige Aktivitäten ausführen auf der Site (bewegen Sie eine Maus oder geben Sie eine Taste ein).

Maryan
quelle
3
Für MVC ist dies meiner Meinung nach die bessere Antwort. Keine Notwendigkeit, .ashx-Datei zu verwenden, warum sollten Sie?
Arame3333
Mit HTTP Handler in asp mvc überprüfen Sie dies , Hoffnung hilft jemandem.
Shaijut
3
Maryan, Sie möchten möglicherweise auch @Url.Action("KeepSessionAlive","Home")die Initialisierungsfunktion verwenden, damit Sie die URL nicht fest codieren und den ersten Block in ein IIFE werfen und nur den exportieren müssen, SetupSessionUpdaterda dies das einzige ist, das extern aufgerufen werden muss - so etwas wie dies: SessionUpdater.js
KyleMit
Gibt es einen Grund, warum setInterval nicht verwendet wird? setInterval(KeepSessionAlive, 300000)
Matthieu Cormier
8

Immer wenn Sie eine Anfrage an den Server stellen, wird das Sitzungszeitlimit zurückgesetzt. Sie können also einfach einen Ajax-Aufruf an einen leeren HTTP-Handler auf dem Server senden, aber sicherstellen, dass der Cache des Handlers deaktiviert ist, da sonst der Browser Ihren Handler zwischenspeichert und keine neue Anforderung stellt.

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 - Der Aufwand für das Speichern von Variablen in der Sitzung ist nicht erforderlich. Es reicht aus, nur eine Anfrage an den Server zu senden.

BornToCode
quelle
2

Müssen Sie die Sitzung wirklich beibehalten (haben Sie Daten darin?) Oder reicht es aus, dies zu fälschen, indem Sie die Sitzung erneut aktivieren, wenn eine Anfrage eingeht? Verwenden Sie im ersten Fall die oben beschriebene Methode. Wenn dies der zweite Fall ist, versuchen Sie es mit dem Session_End-Ereignishandler.

Wenn Sie über eine Formularauthentifizierung verfügen, erhalten Sie in der Datei Global.asax.cs Folgendes

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

Und Sie legen die Ticketlebensdauer viel länger als die Sitzungslebensdauer fest. Wenn Sie sich nicht authentifizieren oder eine andere Authentifizierungsmethode verwenden, gibt es ähnliche Tricks. Microsoft TFS-Weboberfläche und SharePoint scheinen diese zu verwenden. Wenn Sie auf einen Link auf einer veralteten Seite klicken, werden im Popup-Fenster Authentifizierungsaufforderungen angezeigt. Wenn Sie jedoch nur einen Befehl verwenden, funktioniert dies.

Henry Troup
quelle
2

Sie können diesen Code einfach in Ihre Java-Skriptdatei schreiben.

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

Das (20-1)*60*1000ist Refresh - Zeit, es wird das Session - Timeout aktualisieren. Das Aktualisierungszeitlimit wird als Standardzeit von iis = 20 Minuten berechnet, dh 20 × 60000 = 1200000 Millisekunden - 60000 Millisekunden (eine Minute vor Ablauf der Sitzung) sind 1140000.

ANKIT KISHOR
quelle
0

Hier ist eine alternative Lösung, die überleben sollte, wenn der Client-PC in den Ruhemodus wechselt.

Wenn Sie eine große Anzahl angemeldeter Benutzer haben, gehen Sie vorsichtig damit um, da dies viel Serverspeicher beanspruchen kann.

Nachdem Sie sich angemeldet haben (ich mache dies im LoggedIn-Ereignis des Login-Steuerelements)

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
quelle
Warum wurde dies abgelehnt? Gibt es ein Problem damit, das ich nicht erwähnt habe? Wenn dies der Fall ist, teilen Sie dies bitte mit, damit zukünftige Leser davon erfahren!
Peter
0

Ich habe einige Tage damit verbracht, herauszufinden, wie eine Benutzersitzung in WebForms über ein Popup-Dialogfeld verlängert werden kann, in dem der Benutzer die Möglichkeit hat, die Sitzung zu erneuern oder ablaufen zu lassen. Die Nummer 1, die Sie wissen müssen, ist, dass Sie nichts von diesem ausgefallenen 'HttpContext'-Zeug brauchen, das in einigen der anderen Antworten vor sich geht. Alles was Sie brauchen ist jQuerys $ .post (); Methode. Zum Beispiel habe ich beim Debuggen Folgendes verwendet:

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

und auf Ihrer Live-Site würden Sie etwas verwenden wie:

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

So einfach ist das. Wenn Sie den Benutzer mit der Option zur Erneuerung seiner Sitzung auffordern möchten, gehen Sie wie folgt vor:

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

Vergessen Sie nicht, den Dialog zu Ihrem HTML hinzuzufügen:

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

Wenn Sie in Bezug auf die veggerby-Lösung versuchen, sie in einer VB-App zu implementieren, versuchen Sie vorsichtig, den bereitgestellten Code über einen Übersetzer auszuführen. Folgendes wird funktionieren:

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

Anstatt wie heartbeat () aufzurufen, funktionieren Sie auch wie folgt:

 setTimeout("heartbeat()", 300000);

Nennen Sie es stattdessen wie folgt:

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

Nummer eins, setTimeout wird nur einmal ausgelöst, während setInterval wiederholt ausgelöst wird. Nummer zwei, das Aufrufen von heartbeat () wie ein String hat bei mir nicht funktioniert, während das Aufrufen wie eine tatsächliche Funktion funktioniert hat.

Und ich kann absolut zu 100% bestätigen, dass diese Lösung GoDaddys lächerliche Entscheidung überwinden wird, eine 5-minütige Apppool-Sitzung in Plesk zu erzwingen!

ShowJX1990
quelle
0

Hier JQuery Plugin Version der Maryan Lösung mit Handle Optimierung. Nur mit 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)

und wie es in Ihre * .cshtml importiert

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

[Spät zur Party ...]

Eine andere Möglichkeit, dies ohne den Aufwand eines Ajax-Aufrufs oder eines WebService-Handlers zu tun, besteht darin, eine spezielle ASPX-Seite nach einer bestimmten Zeit (dh vor dem Zeitlimit für den Sitzungsstatus, das normalerweise 20 Minuten beträgt) zu laden:

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

Die KeepAlive.aspxSeite ist einfach eine leere Seite, die nur den SessionStatus berührt / aktualisiert :

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

Dies funktioniert, indem ein img(Bild-) Element erstellt und der Browser gezwungen wird, seinen Inhalt von der KeepAlive.aspxSeite zu laden . Durch das Laden dieser Seite berührt (aktualisiert) der Server das SessionObjekt und verlängert das Ablauffenster der Sitzung (normalerweise um weitere 20 Minuten). Der eigentliche Webseiteninhalt wird vom Browser verworfen.

Eine alternative und vielleicht sauberere Möglichkeit, dies zu tun, besteht darin, ein neues iframeElement zu erstellen und die KeepAlive.aspxSeite darin zu laden . Das iframeElement wird ausgeblendet, z. B. indem es divirgendwo auf der Seite zu einem untergeordneten Element eines ausgeblendeten Elements gemacht wird.

Aktivitäten auf der Seite selbst können durch Abfangen von Maus- und Tastaturaktionen für den gesamten Seitenkörper erkannt werden:

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

Ich kann diese Idee nicht würdigen. Siehe: https://www.codeproject.com/Articles/227382/Alert-Session-Time-out-in-ASP-Net .

David R Tribble
quelle