BenutzerPraesenzPruefer
Bei OpenID Connect kann die Cookie-Laufzeit ablaufen, während der Benutzer auf der Seite bleibt — Ajax-Calls laufen danach ins Leere, und auch ein Post-Request scheitert, wenn zwischen Eingabe und Absenden eine längere Pause liegt. Die Komponente prüft das Cookie deshalb in einem Intervall gegen einen Endpunkt der Anwendung und erneuert es über OpenID Connect. Bleibt der Benutzer über mehrere Intervalle untätig, erscheint der Dialog «Sind Sie noch da?».
Gerendert wird sie über @Html.Fmh(Fmh.UserPresenceCheck().Einstellungen(...)) —
üblicherweise einmalig im Layout, damit sie auf jeder Seite läuft.
Aktiviert – Einstellungen aus der appsettings.json
Der Normalfall: Die Einstellungen kommen per IOptions aus der Konfiguration. In dieser Demo sind die Intervalle bewusst kurz, der Dialog erscheint nach rund 10 Sekunden ohne Klick oder Scrollen.
@inject IOptions<BenutzerPraesenzPrueferSetting> BenutzerPraesenzSetting
@* Die Einstellungen stammen aus der Section "BenutzerPraesenzPruefer" der appsettings.json und sind
in Program.cs über services.AddBenutzerPraesenzPruefer(builder.Configuration) registriert.
In einem Projekt steht dieser Aufruf einmalig im Layout. *@
@Html.Fmh(Fmh.UserPresenceCheck().Einstellungen(BenutzerPraesenzSetting.Value))
@* Der Countdown gehört nicht zur Komponente, sondern nur zu dieser Demo: Er liest den Zähler
window.polStep mit, den die Komponente je Intervall hochzählt, und rechnet die Restzeit aus.
Die Umsetzung steht im Tab "JavaScript". *@
<div class="alert alert-info py-2 px-3 mb-3"
data-praesenz-counter
data-praesenz-interval="@BenutzerPraesenzSetting.Value.IntervalInSekunden"
data-praesenz-wiederholungen="@BenutzerPraesenzSetting.Value.Wiederholungen">
<span class="small" data-praesenz-text>Countdown startet …</span>
<div class="progress mt-2" style="height: .4rem;">
<div class="progress-bar" data-praesenz-balken style="width: 100%;"></div>
</div>
<span class="small text-muted">Jeder Klick und jedes Scrollen setzt den Zähler zurück – auch auf dieser Seite.</span>
</div>
<p class="mb-1">Sichtbar ist die Komponente erst, wenn sie zuschlägt: Gerendert werden nur die beiden
Dialoge, die Prüfung läuft im Hintergrund.</p>
<ul class="small mb-0">
<li>Prüfung alle <strong>@BenutzerPraesenzSetting.Value.IntervalInSekunden s</strong> gegen
<code>@BenutzerPraesenzSetting.Value.PruefEndpunkt</code></li>
<li>Dialog «Sind Sie noch da?» nach <strong>@BenutzerPraesenzSetting.Value.Wiederholungen</strong>
Intervallen ohne Klick oder Scrollen — hier also nach rund
@(BenutzerPraesenzSetting.Value.IntervalInSekunden * BenutzerPraesenzSetting.Value.Wiederholungen) Sekunden</li>
<li>«Abmelden» im Dialog führt auf <code>@BenutzerPraesenzSetting.Value.LogoutUrl</code></li>
</ul>
<script src="~/js/demos/benutzer-praesenz-countdown.js" asp-append-version="true"></script>
using Microsoft.AspNetCore.Mvc;
namespace FMH.Komponente.WebUI.Areas.BenutzerPraesenzCheck.Controllers.Beispiele
{
[Area("BenutzerPraesenzCheck")]
public class PraesenzPruefungController : Controller
{
/// <summary>
/// Steht für den <c>PruefEndpunkt</c> der Einstellungen. In einem Projekt ist diese Action
/// Teil des Authorize-Prozesses – läuft das Cookie ab, antwortet sie mit dem Redirect auf
/// den IDP statt mit Json, und genau daran erkennt die Komponente den Erneuerungsbedarf.
/// Die Demo-Anwendung kennt keine Anmeldung, hier antwortet der Endpunkt immer gültig.
/// </summary>
/// <returns>Ein Json-Resultat; der Inhalt ist für die Prüfung ohne Bedeutung.</returns>
[HttpGet]
public IActionResult Status()
{
return this.Json(new { status = "ok" });
}
/// <summary>
/// Steht für die <c>LogoutUrl</c> der Einstellungen: Diese Adresse ruft der Dialog
/// «Sind Sie noch da?» auf, wenn der Benutzer «Abmelden» wählt.
/// </summary>
/// <returns>Die Demo-Seite anstelle der echten Abmeldung.</returns>
[HttpGet]
public IActionResult Abmeldung()
{
return this.View();
}
}
}
// Demo "BenutzerPraesenzCheck": Countdown bis zum Dialog «Sind Sie noch da?».
//
// Die Komponente zählt in window.polStep die Intervalle ohne Interaktion und öffnet den Dialog,
// sobald der Zähler die konfigurierten Wiederholungen erreicht. Der Countdown liest diesen Zähler
// nur mit – er greift nicht in die Komponente ein und gehört ausschliesslich zur Demo.
//
// Intervall und Wiederholungen stehen als data-Attribute am Anzeige-Element und stammen aus den
// Einstellungen der appsettings.json.
document.addEventListener('DOMContentLoaded', function () {
var anzeige = document.querySelector('[data-praesenz-counter]');
if (anzeige === null) {
return;
}
var intervalMs = Number(anzeige.dataset.praesenzInterval) * 1000;
var wiederholungen = Number(anzeige.dataset.praesenzWiederholungen);
var text = anzeige.querySelector('[data-praesenz-text]');
var balken = anzeige.querySelector('[data-praesenz-balken]');
if (intervalMs <= 0 || wiederholungen <= 0) {
return;
}
var letzterSchritt = 0;
// Die Komponente startet ihr setInterval beim document.ready, also praktisch jetzt.
var letzterTick = Date.now();
setInterval(function () {
var schritt = typeof window.polStep === 'number' ? window.polStep : 0;
// Nur das Hochzählen ist ein Intervall-Tick. Das Zurücksetzen auf 0 durch Klick oder
// Scrollen ändert den Takt nicht – der setInterval der Komponente läuft unverändert weiter.
if (schritt > letzterSchritt) {
letzterTick = Date.now();
}
letzterSchritt = schritt;
var offeneTicks = wiederholungen - schritt;
if (offeneTicks <= 0) {
text.textContent = 'Der Dialog ist offen – «Weiter» lädt die Seite neu, «Abmelden» führt auf die LogoutUrl.';
balken.style.width = '0%';
return;
}
var bisNaechsterTick = Math.max(0, intervalMs - (Date.now() - letzterTick));
var restMs = (offeneTicks - 1) * intervalMs + bisNaechsterTick;
text.textContent = 'Dialog «Sind Sie noch da?» in ' + (restMs / 1000).toFixed(1) + ' s – '
+ offeneTicks + ' von ' + wiederholungen + ' Intervallen offen.';
balken.style.width = Math.round(restMs / (wiederholungen * intervalMs) * 100) + '%';
}, 200);
});
// jQuery-Variante zum Countdown des Beispiels «Aktiviert» – dasselbe wie im Tab "JavaScript",
// nur mit jQuery geschrieben. Diese Datei wird von der Demo-Seite NICHT geladen; sonst liefen zwei
// Countdowns gleichzeitig. Sie ist als Vorlage zum Übernehmen gedacht.
$(function () {
var $anzeige = $('[data-praesenz-counter]');
if ($anzeige.length === 0) {
return;
}
// .data() liest die data-Attribute in camelCase: data-praesenz-interval -> praesenzInterval.
var intervalMs = Number($anzeige.data('praesenzInterval')) * 1000;
var wiederholungen = Number($anzeige.data('praesenzWiederholungen'));
var $text = $anzeige.find('[data-praesenz-text]');
var $balken = $anzeige.find('[data-praesenz-balken]');
if (intervalMs <= 0 || wiederholungen <= 0) {
return;
}
var letzterSchritt = 0;
// Die Komponente startet ihr setInterval beim document.ready, also praktisch jetzt.
var letzterTick = Date.now();
window.setInterval(function () {
var schritt = typeof window.polStep === 'number' ? window.polStep : 0;
// Nur das Hochzählen ist ein Intervall-Tick. Das Zurücksetzen auf 0 durch Klick oder
// Scrollen ändert den Takt nicht – der setInterval der Komponente läuft unverändert weiter.
if (schritt > letzterSchritt) {
letzterTick = Date.now();
}
letzterSchritt = schritt;
var offeneTicks = wiederholungen - schritt;
if (offeneTicks <= 0) {
$text.text('Der Dialog ist offen – «Weiter» lädt die Seite neu, «Abmelden» führt auf die LogoutUrl.');
$balken.css('width', '0%');
return;
}
var bisNaechsterTick = Math.max(0, intervalMs - (Date.now() - letzterTick));
var restMs = (offeneTicks - 1) * intervalMs + bisNaechsterTick;
$text.text('Dialog «Sind Sie noch da?» in ' + (restMs / 1000).toFixed(1) + ' s – '
+ offeneTicks + ' von ' + wiederholungen + ' Intervallen offen.');
$balken.css('width', Math.round(restMs / (wiederholungen * intervalMs) * 100) + '%');
}, 200);
});
Darstellung
Sichtbar ist die Komponente erst, wenn sie zuschlägt: Gerendert werden nur die beiden Dialoge, die Prüfung läuft im Hintergrund.
- Prüfung alle 2 s gegen
/BenutzerPraesenzCheck/PraesenzPruefung/Status - Dialog «Sind Sie noch da?» nach 5 Intervallen ohne Klick oder Scrollen — hier also nach rund 10 Sekunden
- «Abmelden» im Dialog führt auf
/BenutzerPraesenzCheck/PraesenzPruefung/Abmeldung
Der BenuterPraesenzPrueferBuilder wird über Fmh.UserPresenceCheck() erzeugt und mit @Html.Fmh(...) gerendert — üblicherweise einmal im Layout, damit die Prüfung auf jeder Seite läuft.
@inject IOptions<BenutzerPraesenzPrueferSetting> BenutzerPraesenzSetting
@Html.Fmh(Fmh.UserPresenceCheck().Einstellungen(BenutzerPraesenzSetting.Value))
Wozu die Komponente da ist
Bei OpenID Connect kann die Cookie-Laufzeit ablaufen, während der Benutzer auf der Seite bleibt. Ein Ajax-Call läuft danach ins Leere; dasselbe passiert bei einem Post-Request, wenn zwischen Eingabe und Absenden eine längere Pause liegt — die Eingaben sind verloren.
Die Komponente hält dagegen einen Zähler: In einem festen Intervall ruft sie einen Endpunkt der Anwendung auf und erneuert das Cookie über OpenID Connect, falls nötig. Jeder Klick und jedes Scrollen setzt den Zähler zurück. Bleibt der Benutzer über die konfigurierte Anzahl Intervalle untätig, stoppt die Prüfung und der Dialog «Sind Sie noch da?» erscheint.
Einstellungen registrieren (Program.cs)
Die Einstellungen kommen aus der appsettings.json und werden mit einer Erweiterungsmethode der Komponente registriert. Sie bindet die Section, macht sie über IOptions<BenutzerPraesenzPrueferSetting> injizierbar und protokolliert die gelesenen Werte beim Start:
using FMH.Komponente.BenuterPraesenzCheck;
builder.Services.AddBenutzerPraesenzPruefer(builder.Configuration);
Damit entfällt die frühere Handarbeit (Configure<…>, GetSection(…).Get<…>(), LogSetting()). Fehlt die Section in der Konfiguration, gelten die Standardwerte der Einstellungsklasse.
appsettings.json
"BenutzerPraesenzPruefer": {
"IntervalInSekunden": 30,
"Wiederholungen": 8,
"LogoutUrl": "/Logout/Logout",
"PruefEndpunkt": "/BenutzerPraesenzPruefung/Status",
"IstAktiviert": true,
"AuthCheckIntervalInSekunden": 45,
"IstAuthCheckAktiviert": false
}
| Einstellung | Standard | Erklärung |
|---|---|---|
IntervalInSekunden |
30 |
Zeitdauer, nach der der Prüfendpunkt kontaktiert wird, um die Gültigkeit des Cookies festzustellen. Im Fehlerfall läuft die Erneuerung über OpenID Connect. Auf die Cookie-Laufzeit der Anwendung abstimmen. |
Wiederholungen |
8 |
Anzahl Intervalle ohne Interaktion, bis der Dialog «Sind Sie noch da?» erscheint. Jeder Klick und jedes Scrollen setzt den Zähler auf 0 zurück. IntervalInSekunden × Wiederholungen ergibt die geduldete Untätigkeit. |
LogoutUrl |
/Logout/LogOut |
Endpunkt für das Abmelden — wird aufgerufen, wenn der Benutzer im Dialog «Abmelden» wählt. |
PruefEndpunkt |
/Home/Status |
Adresse, gegen die geprüft wird. Muss im Authorize-Prozess liegen und ein Json-Resultat liefern; der Inhalt ist ohne Bedeutung. |
IstAktiviert |
true |
Schaltet die Komponente ein oder aus. Deaktiviert erscheint im Quelltext nur der Kommentar <!-- nupce -->, es läuft kein Skript. |
IstAuthCheckAktiviert |
false |
Zusätzlicher Prüflauf des Authentifizierungsstatus, unabhängig von der Untätigkeit. Hilft bei Benutzern, die in mehreren Browser-Tabs arbeiten und sich in einem davon abmelden — die übrigen Tabs zeigen dann den Dialog, statt erst bei der nächsten Eingabe zu scheitern. |
AuthCheckIntervalInSekunden |
45 |
Intervall dieses zusätzlichen Prüflaufs. Wirkt nur bei IstAuthCheckAktiviert = true. |
Builder-Methoden
| Methode | Erklärung |
|---|---|
.Einstellungen(BenutzerPraesenzPrueferSetting) |
Übergibt die Konfiguration. Ohne Angabe gelten die Standardwerte; null führt zu einer ArgumentException. |
.Mit / .With |
Lesefreundliche Verkettung ohne eigene Wirkung (Fmh.UserPresenceCheck().Mit.Einstellungen(...)). |
Anforderungen an den Prüfendpunkt
Der PruefEndpunkt ist eine gewöhnliche Action der Anwendung — mehr als diese paar Zeilen braucht es nicht:
[Authorize]
[HttpGet]
public IActionResult Status()
{
return this.Json(new { status = "ok" });
}
- Die Action muss im Authorize-Prozess liegen: Nur dann antwortet sie bei abgelaufenem Cookie mit der Anmelde-Umleitung, an der die Komponente den Erneuerungsbedarf erkennt. Ohne
[Authorize]meldet der Endpunkt immer «gültig» und die Erneuerung läuft nie an. - Sie muss ein Json-Resultat zurückgeben (
return this.Json(...)); der Inhalt spielt keine Rolle. - Antwortet der Endpunkt mit
404, bricht die Komponente mit der Meldung «Konfiguration UserPresenseCheck prüfen.» ab — dann stimmt die Adresse inPruefEndpunktnicht.
Der Tab «Controller (Prüfendpunkt)» am Beispiel zeigt die Umsetzung dieser Demo. Sie kommt ohne [Authorize] aus, weil die Demo-Anwendung keine Anmeldung kennt — in einem Projekt gehört das Attribut zwingend dazu.
IDP-Konfiguration (CORS)
Läuft die Anwendung unter einer neuen Domain, muss diese im IDP in der appsettings.json unter CORS.Domain eingetragen werden. Fehlt der Eintrag, zeigt sich das als JavaScript-Fehler mit responseText undefined.
Was der Benutzer sieht
- «Sind Sie noch da?» — nach
WiederholungenIntervallen ohne Interaktion. «Weiter» lädt die Seite neu (und erneuert damit die Anmeldung), «Abmelden» führt auf dieLogoutUrl. - Nicht mehr angemeldet — wenn die Erneuerung scheitert oder der zusätzliche Auth-Check eine abgelaufene Anmeldung feststellt. Der Dialog lässt sich nicht wegklicken; er bietet nur die erneute Anmeldung an.
Beide Dialoge tragen feste Ids (dialog-inactive, dialog-unauthenticated). Die Komponente gehört deshalb genau einmal auf eine Seite.
Zu dieser Demo
Die Demo-Anwendung kennt keine Anmeldung: Der Prüfendpunkt (PraesenzPruefungController.Status) antwortet immer gültig, und «Abmelden» führt auf eine Erklärseite statt auf einen echten Logout. Die Intervalle stehen in der appsettings.json bewusst kurz (2 Sekunden, 5 Wiederholungen), damit der Dialog nach rund 10 Sekunden ohne Klick oder Scrollen erscheint — in einem Projekt sind Werte in der Grössenordnung von Minuten üblich.
Der Countdown über dem Beispiel gehört ebenfalls nur zur Demo. Er liest den Zähler window.polStep, den die Komponente je Intervall hochzählt, und rechnet daraus die Restzeit ((Wiederholungen − polStep) × IntervalInSekunden). Damit lässt sich mitverfolgen, wie jeder Klick und jedes Scrollen den Zähler auf 0 zurücksetzt. Das Skript zeigt der Code-Tab JavaScript am Beispiel, der Tab jQuery dieselbe Umsetzung mit jQuery; beide Dateien liegen unter wwwroot/js/demos/ der Demo-Anwendung, geladen wird nur die JavaScript-Fassung. Die Komponente selbst bringt keinen solchen Zähler mit.
Deaktiviert über IstAktiviert = false
Ohne Aktivierung erzeugt der Builder weder Dialoge noch Skript – im Quelltext steht nur der Kommentar <!-- nupce -->.
@* IstAktiviert = false schaltet die Komponente ab: kein Dialog, kein Skript, keine Prüfläufe.
In einem Projekt kommt der Wert aus der appsettings.json; hier steht er direkt im Beispiel,
damit beide Zustände auf derselben Seite sichtbar sind. *@
@{
var deaktiviert = new BenutzerPraesenzPrueferSetting { IstAktiviert = false };
}
@Html.Fmh(Fmh.UserPresenceCheck().Einstellungen(deaktiviert))
<p class="mb-0">Ausgabe an dieser Stelle ist nur der Kommentar <code><!-- nupce --></code>
im Quelltext der Seite — sichtbar über «Seitenquelltext anzeigen».</p>
Darstellung
Ausgabe an dieser Stelle ist nur der Kommentar <!-- nupce -->
im Quelltext der Seite — sichtbar über «Seitenquelltext anzeigen».
Der BenuterPraesenzPrueferBuilder wird über Fmh.UserPresenceCheck() erzeugt und mit @Html.Fmh(...) gerendert — üblicherweise einmal im Layout, damit die Prüfung auf jeder Seite läuft.
@inject IOptions<BenutzerPraesenzPrueferSetting> BenutzerPraesenzSetting
@Html.Fmh(Fmh.UserPresenceCheck().Einstellungen(BenutzerPraesenzSetting.Value))
Wozu die Komponente da ist
Bei OpenID Connect kann die Cookie-Laufzeit ablaufen, während der Benutzer auf der Seite bleibt. Ein Ajax-Call läuft danach ins Leere; dasselbe passiert bei einem Post-Request, wenn zwischen Eingabe und Absenden eine längere Pause liegt — die Eingaben sind verloren.
Die Komponente hält dagegen einen Zähler: In einem festen Intervall ruft sie einen Endpunkt der Anwendung auf und erneuert das Cookie über OpenID Connect, falls nötig. Jeder Klick und jedes Scrollen setzt den Zähler zurück. Bleibt der Benutzer über die konfigurierte Anzahl Intervalle untätig, stoppt die Prüfung und der Dialog «Sind Sie noch da?» erscheint.
Einstellungen registrieren (Program.cs)
Die Einstellungen kommen aus der appsettings.json und werden mit einer Erweiterungsmethode der Komponente registriert. Sie bindet die Section, macht sie über IOptions<BenutzerPraesenzPrueferSetting> injizierbar und protokolliert die gelesenen Werte beim Start:
using FMH.Komponente.BenuterPraesenzCheck;
builder.Services.AddBenutzerPraesenzPruefer(builder.Configuration);
Damit entfällt die frühere Handarbeit (Configure<…>, GetSection(…).Get<…>(), LogSetting()). Fehlt die Section in der Konfiguration, gelten die Standardwerte der Einstellungsklasse.
appsettings.json
"BenutzerPraesenzPruefer": {
"IntervalInSekunden": 30,
"Wiederholungen": 8,
"LogoutUrl": "/Logout/Logout",
"PruefEndpunkt": "/BenutzerPraesenzPruefung/Status",
"IstAktiviert": true,
"AuthCheckIntervalInSekunden": 45,
"IstAuthCheckAktiviert": false
}
| Einstellung | Standard | Erklärung |
|---|---|---|
IntervalInSekunden |
30 |
Zeitdauer, nach der der Prüfendpunkt kontaktiert wird, um die Gültigkeit des Cookies festzustellen. Im Fehlerfall läuft die Erneuerung über OpenID Connect. Auf die Cookie-Laufzeit der Anwendung abstimmen. |
Wiederholungen |
8 |
Anzahl Intervalle ohne Interaktion, bis der Dialog «Sind Sie noch da?» erscheint. Jeder Klick und jedes Scrollen setzt den Zähler auf 0 zurück. IntervalInSekunden × Wiederholungen ergibt die geduldete Untätigkeit. |
LogoutUrl |
/Logout/LogOut |
Endpunkt für das Abmelden — wird aufgerufen, wenn der Benutzer im Dialog «Abmelden» wählt. |
PruefEndpunkt |
/Home/Status |
Adresse, gegen die geprüft wird. Muss im Authorize-Prozess liegen und ein Json-Resultat liefern; der Inhalt ist ohne Bedeutung. |
IstAktiviert |
true |
Schaltet die Komponente ein oder aus. Deaktiviert erscheint im Quelltext nur der Kommentar <!-- nupce -->, es läuft kein Skript. |
IstAuthCheckAktiviert |
false |
Zusätzlicher Prüflauf des Authentifizierungsstatus, unabhängig von der Untätigkeit. Hilft bei Benutzern, die in mehreren Browser-Tabs arbeiten und sich in einem davon abmelden — die übrigen Tabs zeigen dann den Dialog, statt erst bei der nächsten Eingabe zu scheitern. |
AuthCheckIntervalInSekunden |
45 |
Intervall dieses zusätzlichen Prüflaufs. Wirkt nur bei IstAuthCheckAktiviert = true. |
Builder-Methoden
| Methode | Erklärung |
|---|---|
.Einstellungen(BenutzerPraesenzPrueferSetting) |
Übergibt die Konfiguration. Ohne Angabe gelten die Standardwerte; null führt zu einer ArgumentException. |
.Mit / .With |
Lesefreundliche Verkettung ohne eigene Wirkung (Fmh.UserPresenceCheck().Mit.Einstellungen(...)). |
Anforderungen an den Prüfendpunkt
Der PruefEndpunkt ist eine gewöhnliche Action der Anwendung — mehr als diese paar Zeilen braucht es nicht:
[Authorize]
[HttpGet]
public IActionResult Status()
{
return this.Json(new { status = "ok" });
}
- Die Action muss im Authorize-Prozess liegen: Nur dann antwortet sie bei abgelaufenem Cookie mit der Anmelde-Umleitung, an der die Komponente den Erneuerungsbedarf erkennt. Ohne
[Authorize]meldet der Endpunkt immer «gültig» und die Erneuerung läuft nie an. - Sie muss ein Json-Resultat zurückgeben (
return this.Json(...)); der Inhalt spielt keine Rolle. - Antwortet der Endpunkt mit
404, bricht die Komponente mit der Meldung «Konfiguration UserPresenseCheck prüfen.» ab — dann stimmt die Adresse inPruefEndpunktnicht.
Der Tab «Controller (Prüfendpunkt)» am Beispiel zeigt die Umsetzung dieser Demo. Sie kommt ohne [Authorize] aus, weil die Demo-Anwendung keine Anmeldung kennt — in einem Projekt gehört das Attribut zwingend dazu.
IDP-Konfiguration (CORS)
Läuft die Anwendung unter einer neuen Domain, muss diese im IDP in der appsettings.json unter CORS.Domain eingetragen werden. Fehlt der Eintrag, zeigt sich das als JavaScript-Fehler mit responseText undefined.
Was der Benutzer sieht
- «Sind Sie noch da?» — nach
WiederholungenIntervallen ohne Interaktion. «Weiter» lädt die Seite neu (und erneuert damit die Anmeldung), «Abmelden» führt auf dieLogoutUrl. - Nicht mehr angemeldet — wenn die Erneuerung scheitert oder der zusätzliche Auth-Check eine abgelaufene Anmeldung feststellt. Der Dialog lässt sich nicht wegklicken; er bietet nur die erneute Anmeldung an.
Beide Dialoge tragen feste Ids (dialog-inactive, dialog-unauthenticated). Die Komponente gehört deshalb genau einmal auf eine Seite.
Zu dieser Demo
Die Demo-Anwendung kennt keine Anmeldung: Der Prüfendpunkt (PraesenzPruefungController.Status) antwortet immer gültig, und «Abmelden» führt auf eine Erklärseite statt auf einen echten Logout. Die Intervalle stehen in der appsettings.json bewusst kurz (2 Sekunden, 5 Wiederholungen), damit der Dialog nach rund 10 Sekunden ohne Klick oder Scrollen erscheint — in einem Projekt sind Werte in der Grössenordnung von Minuten üblich.
Der Countdown über dem Beispiel gehört ebenfalls nur zur Demo. Er liest den Zähler window.polStep, den die Komponente je Intervall hochzählt, und rechnet daraus die Restzeit ((Wiederholungen − polStep) × IntervalInSekunden). Damit lässt sich mitverfolgen, wie jeder Klick und jedes Scrollen den Zähler auf 0 zurücksetzt. Das Skript zeigt der Code-Tab JavaScript am Beispiel, der Tab jQuery dieselbe Umsetzung mit jQuery; beide Dateien liegen unter wwwroot/js/demos/ der Demo-Anwendung, geladen wird nur die JavaScript-Fassung. Die Komponente selbst bringt keinen solchen Zähler mit.