Meistens ist es Ihnen egal, ob ein Benutzer in Ihrer Anwendung aktiv oder vorübergehend inaktiv ist. Inaktiv bedeutet, vielleicht ist er aufgestanden, um sich ein Getränk zu holen, oder wahrscheinlicher, hat zu einem anderen Tab gewechselt, um etwas anderes zu tun. Es gibt jedoch Situationen, in denen die Verfolgung der Benutzeraktivität und die Erkennung von Inaktivität nützlich sein können.
Denken wir über ein paar Beispiele nach, wann Sie diese Funktionalität vielleicht benötigen würden
- Verfolgung der Lesezeit von Artikeln
- Automatisches Speichern von Formularen oder Dokumenten
- Automatisches Pausieren von Spielen
- Ausblenden von Videoplayer-Steuerelementen
- Automatische Abmeldung von Benutzern aus Sicherheitsgründen
Ich bin kürzlich auf eine Funktion gestoßen, die sich mit dem letzten Beispiel befasste: automatische Abmeldung inaktiver Benutzer aus Sicherheitsgründen.
Warum sollten wir uns um die automatische Abmeldung kümmern?
Viele Anwendungen gewähren Benutzern Zugriff auf einige ihrer persönlichen Daten. Je nach Zweck der Anwendung können Menge und Wert dieser Daten unterschiedlich sein. Es kann nur der Name des Benutzers sein, aber es können auch sensiblere Daten sein, wie medizinische Unterlagen, Finanzunterlagen usw.
Es besteht die Möglichkeit, dass einige Benutzer vergessen, sich abzumelden, und die Sitzung offen lassen. Wie oft ist Ihnen das passiert? Vielleicht klingelte Ihr Telefon plötzlich oder Sie mussten sofort gehen und ließen den Browser geöffnet. Das Hinterlassen einer offenen Benutzersitzung ist gefährlich, da jemand anderes diese Sitzung nutzen könnte, um sensible Daten zu extrahieren.
Ein Weg, dieses Problem zu bekämpfen, beinhaltet die Überwachung, ob der Benutzer innerhalb eines bestimmten Zeitraums mit der App interagiert hat, und dann die Auslösung der Abmeldung, wenn diese Zeit überschritten ist. Sie möchten möglicherweise ein Popover anzeigen oder vielleicht einen Timer, der den Benutzer warnt, dass die Abmeldung bevorsteht. Oder Sie melden sich einfach sofort ab, wenn ein inaktiver Benutzer erkannt wird.
Gehen wir eine Ebene tiefer: Was wir tun wollen, ist, die seit der letzten Interaktion des Benutzers vergangene Zeit zu zählen. Wenn dieser Zeitraum länger als unsere Schwelle ist, möchten wir unseren Inaktivitäts-Handler auslösen. Wenn der Benutzer eine Aktion ausführt, bevor die Schwelle überschritten ist, setzen wir den Zähler zurück und beginnen erneut zu zählen.
Dieser Artikel zeigt, wie wir eine solche Aktivitätsverfolgungslogik basierend auf diesem Beispiel implementieren können.
Schritt 1: Implementierung der Tracking-Logik
Implementieren wir zwei Funktionen. Die erste ist für das Zurücksetzen unseres Timers bei jeder Interaktion des Benutzers mit der App zuständig, und die zweite behandelt die Situation, wenn der Benutzer inaktiv wird.
resetUserActivityTimeout– Dies ist unsere Methode, die für das Löschen des vorhandenen Timeouts und das Starten eines neuen bei jeder Interaktion des Benutzers mit der Anwendung zuständig ist.inactiveUserAction– Dies ist unsere Methode, die ausgelöst wird, wenn der Timeout für die Benutzeraktivität abläuft.
let userActivityTimeout = null;
function resetUserActivityTimeout() {
clearTimeout(userActivityTimeout);
userActivityTimeout = setTimeout(() => {
inactiveUserAction();
}, INACTIVE_USER_TIME_THRESHOLD);
}
function inactiveUserAction() {
// logout logic
}
OK, wir haben also Methoden, die für die Verfolgung der Aktivität zuständig sind, aber wir verwenden sie noch nirgends.
Schritt 2: Aktivierung der Verfolgung
Jetzt müssen wir Methoden implementieren, die für die Aktivierung der Verfolgung zuständig sind. In diesen Methoden fügen wir Event-Listener hinzu, die unsere Methode resetUserActivityTimeout aufrufen, wenn das Ereignis erkannt wird. Sie können auf so viele Ereignisse hören, wie Sie möchten, aber der Einfachheit halber beschränken wir diese Liste auf einige der häufigsten.
function activateActivityTracker() {
window.addEventListener("mousemove", resetUserActivityTimeout);
window.addEventListener("scroll", resetUserActivityTimeout);
window.addEventListener("keydown", resetUserActivityTimeout);
window.addEventListener("resize", resetUserActivityTimeout);
}
Das ist alles. Unsere Benutzerverfolgung ist bereit. Das Einzige, was wir tun müssen, ist, activateActivityTracker beim Laden unserer Seite aufzurufen.
Wir können es so belassen, aber wenn Sie genauer hinschauen, gibt es ein ernsthaftes Performance-Problem mit dem Code, den wir gerade committet haben. Jedes Mal, wenn der Benutzer mit der App interagiert, läuft die gesamte Logik. Das ist gut, aber schauen Sie genauer hin. Es gibt einige Arten von Ereignissen, die enorm oft ausgelöst werden, wenn der Benutzer mit der Seite interagiert, auch wenn dies für unsere Verfolgung nicht notwendig ist. Betrachten wir das mousemove-Ereignis. Selbst wenn Sie Ihre Maus nur leicht bewegen, wird das mousemove-Ereignis dutzende Male ausgelöst. Das ist ein echter Performance-Killer. Wir können dieses Problem beheben, indem wir einen Throttler einführen, der es ermöglicht, die Benutzeraktivitätslogik nur einmal pro angegebenem Zeitraum auszulösen.
Das machen wir jetzt.
Schritt 3: Verbesserung der Performance
Zuerst müssen wir eine weitere Variable hinzufügen, die eine Referenz auf unseren Throttler-Timeout speichert.
let userActivityThrottlerTimeout = null
Dann erstellen wir eine Methode, die unseren Throttler erstellt. In dieser Methode prüfen wir, ob der Throttler-Timeout bereits existiert, und wenn nicht, erstellen wir einen, der resetUserActivityTimeout nach einer bestimmten Zeit auslöst. Das ist die Zeitspanne, für die alle Benutzeraktivitäten die Tracking-Logik nicht erneut auslösen werden. Nach dieser Zeit wird der Throttler-Timeout gelöscht, sodass die nächste Interaktion den Aktivitäts-Tracker zurücksetzen kann.
userActivityThrottler() {
if (!userActivityThrottlerTimeout) {
userActivityThrottlerTimeout = setTimeout(() => {
resetUserActivityTimeout();
clearTimeout(userActivityThrottlerTimeout);
userActivityThrottlerTimeout = null;
}, USER_ACTIVITY_THROTTLER_TIME);
}
}
Wir haben gerade eine neue Methode erstellt, die bei Benutzerinteraktion ausgelöst werden soll, also müssen wir daran denken, die Event-Listener von resetUserActivityTimeout auf userActivityThrottler in unserer Aktivierungslogik zu ändern.
activateActivityTracker() {
window.addEventListener("mousemove", userActivityThrottler);
// ...
}
Bonus: Schauen wir es uns noch einmal an!
Nachdem wir unsere Aktivitätsverfolgungslogik implementiert haben, sehen wir uns an, wie wir diese Logik in eine mit Vue erstellte Anwendung übertragen können. Wir werden die Erklärung auf diesem Beispiel basieren.
Zuerst müssen wir alle Variablen in die data unseres Komponenten verschieben, das ist der Ort, an dem alle reaktiven Props leben.
export default {
data() {
return {
isInactive: false,
userActivityThrottlerTimeout: null,
userActivityTimeout: null
};
},
// ...
Dann verschieben wir alle unsere Funktionen in die methods.
// ...
methods: {
activateActivityTracker() {...},
resetUserActivityTimeout() {...},
userActivityThrottler() {...},
inactiveUserAction() {...}
},
// ...
Da wir Vue und sein reaktives System verwenden, können wir alle direkten DOM-Manipulationen (z.B. document.getElementById("app").innerHTML) fallen lassen und uns auf unsere isInactive-Daten-Eigenschaft verlassen. Wir können direkt in der Vorlage unserer Komponente auf die Daten-Eigenschaft zugreifen, wie unten gezeigt.
<template>
<div id="app">
<p>User is inactive = {{ isInactive }}</p>
</div>
</template>
Das Letzte, was wir tun müssen, ist, einen geeigneten Ort zu finden, um die Tracking-Logik zu aktivieren. Vue bietet Komponenten- Lifecycle-Hooks, die genau das sind, was wir brauchen – insbesondere den beforeMount-Hook. Also packen wir es dort hinein.
// ...
beforeMount() {
this.activateActivityTracker();
},
// ...
Es gibt noch eine weitere Sache, die wir tun können. Da wir Timeouts verwenden und Event-Listener am Fenster registrieren, ist es immer eine gute Praxis, ein wenig aufzuräumen. Wir können dies in einem anderen Lifecycle-Hook tun, beforeDestroy. Lassen Sie uns alle Listener entfernen, die wir registriert haben, und alle Timeouts löschen, wenn der Lebenszyklus der Komponente endet.
// ...
beforeDestroy() {
window.removeEventListener("mousemove", this.userActivityThrottler);
window.removeEventListener("scroll", this.userActivityThrottler);
window.removeEventListener("keydown", this.userActivityThrottler);
window.removeEventListener("resize", this.userActivityThrottler);
clearTimeout(this.userActivityTimeout);
clearTimeout(this.userActivityThrottlerTimeout);
}
// ...
Das ist ein Abschluss!
Dieses Beispiel konzentriert sich rein auf die Erkennung der Benutzerinteraktion mit der Anwendung, die Reaktion darauf und das Auslösen einer Methode, wenn über einen bestimmten Zeitraum keine Interaktion erkannt wird. Ich wollte, dass dieses Beispiel so universell wie möglich ist, deshalb überlasse ich Ihnen die Implementierung dessen, was passieren soll, wenn ein inaktiver Benutzer erkannt wird.
Ich hoffe, Sie finden diese Lösung für Ihr Projekt nützlich!
Hallo, Mateusz Rybczonek!
Könnten Sie mir sagen, ob der obige Code für eine Echtzeit-Website oder für experimentelles JS wie normale JS-Programmierung ist?
Entschuldigung für mein schlechtes Englisch.
Hallo Mahesh,
Wie unten erwähnt, ist dieser Code nur ein kleiner Teil der gesamten Funktionalität zur automatischen Abmeldung. Die vollständige Funktion muss sowohl Frontend als auch Backend umfassen.
Ich habe mir ein paar grobe Fälle für die Inaktivitätserkennung vorgestellt, die eine ausgefeiltere Lösung erfordern würden. Was ist, wenn die Maus des Benutzers kaputt ist. Sie macht ständig Mikrobewegungen. Oder ein Benutzer richtet eine Software ein, die dies absichtlich tut. Oder der Benutzer deaktiviert den Teil Ihres Skripts, der für die Erkennung von Inaktivität zuständig ist.
Ich würde Ihre Lösung eher durch eine serverseitige Lösung ergänzen, die prüft, wann der Benutzer zuletzt eine Ressource oder eine Aktion angefordert hat, und dies an die App zurückmeldet (oder auch nicht, sie einfach direkt abmeldet). Natürlich haben die meisten Server eine eingebaute Ablaufzeit für Sitzungen, aber in einigen Fällen möchten Sie so etwas nicht.
Hallo Artur,
Ich stimme zu, dies ist nur ein Anfang und zeigt nur einen Weg, einen inaktiven Benutzer zu erkennen.
Wie Sie erwähnt haben, gibt es normalerweise eine Sitzung auf dem Backend, die nach einer bestimmten Zeit abläuft. Diese Sitzung wird jedes Mal verlängert, wenn der Benutzer einen API-Aufruf tätigt.
Diese Lösung zeigt, wie Sie erkennen können, ob der Benutzer für eine bestimmte Zeit inaktiv war, und auf diese Inaktivität reagieren können. Sie haben zum Beispiel eine Sitzung, die nach 10 Minuten abläuft. Nach 8 Minuten Inaktivität möchten Sie dem Benutzer vielleicht anzeigen, dass die Sitzung abläuft und er abgemeldet wird, wenn keine Aktion erfolgt.
Vielen Dank für Ihren Kommentar.
Dies ist ein guter Anfang, aber aus persönlicher Erfahrung fehlen ein paar wichtige Teile
1. Wenn Sicherheit das Ziel ist, ist die clientseitige automatische Abmeldung nicht ausreichend. Der Server muss die Sitzung nach einer bestimmten Inaktivitätszeit beenden.
2. Der Benutzer kann den Browser schließen oder die Verbindung verlieren, auch aus Sicherheitsgründen sollte dies immer zur Abmeldung des Benutzers führen.
3. Aufgrund von 1 und 2 sind Sie ziemlich gezwungen, einen Dialog „Sind Sie noch da?“ anzuzeigen. Wenn „Ja“ geklickt wird, wird der Server aufgefordert, die Sitzung am Leben zu erhalten. Solange der Benutzer aktiv ist, möchten Sie möglicherweise periodisch den Server anpingen, um mit dem Client synchron zu bleiben.
4. Der Client kann mehrere Tabs geöffnet haben, und Sie müssen die Authentifizierungssynchronisation über diese hinweg gewährleisten. Localstorage funktioniert dafür.
5. Als Fallback, falls Server und Client außer Synchronisation geraten, insbesondere da der Client nicht zuverlässig ist, müssen Sie 400er-Fehler abfangen und behandeln.
Der Inaktivitäts-Timeout ist meiner Erfahrung nach eines dieser Dinge, die einfach erscheinen, aber tatsächlich enorm schwierig korrekt zu implementieren sind.
Ja, die Synchronisierung zwischen Browser-Tabs mit Local Storage scheint eine großartige Idee zu sein.
Hallo Aaron,
Ich stimme Ihrem Kommentar vollkommen zu, dies ist nur ein Anfang und zeigt nur einen Weg, einen inaktiven Benutzer zu erkennen.
Vielen Dank für Ihren Kommentar.
Lösung: Drosseln Sie einfach eine abgedeckte Logout-Methode. Fertig.
Jetzt läuft immer noch Code, der jedes Mal ausgeführt wird, wenn das Ereignis auftritt. Was ist mit der Abmeldung des Event-Listeners?
Hallo Jakob,
Sie können den Unterschied in diesen beiden Sandboxes sehen.
https://codesandbox.io/s/activity-tracker-vanilla-js-events-count-without-throttler-t89gx
https://codesandbox.io/s/activity-tracker-vanilla-js-events-count-with-throttler-4d3hy
Vielen Dank für den Tipp zum Abmelden der Event-Listener, hinzugefügt zu den Beispielen.
Vielen Dank, Mateusz!
Das war wirklich hilfreich :)