Detecting Inactive Users

Avatar of Mateusz Rybczonek
Mateusz Rybczonek am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

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!