Beim Einloggen erinnern

In unser Login-Formular wollen wir noch eine weitere Checkbox mit einbauen. Und zwar wollen wir dem User die Möglichkeit geben, eine Checkbox „Errinnere Dich“ zu aktivieren, um sich nach dem Schließen des Browsers nicht erneut einloggen zu müssen.

Die Technik dahinter ist simpel: Falls der User die Checkbox klickt, passiert gar nichts. Djangos Default-Einstellung ist es nämlch, einen eingeloggten User auch nach Schließen des Browsers nicht erneut zum Einloggen zu zwingen.

Manche User mögen das aber explizit nicht: die können die Checkbox dann deaktiviert lassen und müssen sich bei einem Login nach dem Schließen des Browsers wieder erneut anmelden. Für sicherheitskritische Applikationen (Banken, Verwaltungen) ist dieses Vorgehen ratsam.

Wir müssen in unserer App user jetzt ein paar Anpassungen vornehmen, um dieses Verhalten zu implementieren.

Die LoginView aus den auth_views

Bisher hatten wir für den Login noch keine eigene View geschrieben, sondern nur direkt die auth_views.LoginView aus django.contrib.auth genutzt. Dazu hatten wir diesen Eintrag in den event_manager/user/urls.py angelegt.

from django.contrib.auth import views as auth_views


urlpatterns = [

    path(
        "login/",
        auth_views.LoginView.as_view(redirect_authenticated_user=True),
        name="login",
    ),
 ]

Wir hatten also die Default LoginView gleich direkt genutzt, ohne uns die Arbeit zu machen, sie zu spezialisieren. Das war ja bisher auch nicht notwendig. Die LoginView ist für die Authentifizierung und den Login nötig, und schmeißt einen Fehler, falls der User mit den übergebenen Passwort- und Usernamen nicht angemeldet werden kann.

Die Checkbox-Situation

Nun wollen wir aber eine Checkbox auswerten, die der User im Login-Formular mit an Django übergibt, und zwar die Einstellung, ob sich Django an den User „erinnern“ soll, damit er sich nicht jedes mal neu einloggen muss. Dieses Feature sieht man häufig auf Websites und wird gerne genutzt.

Die LoginView überschreiben

Wir wollen diese LoginView also überschreiben. Dazu öffnen wir event_manager/user/views.py und fügen folgenden Code hinzu:

from django.contrib.auth import views as auth_views
# ... viele andere Imports
from .forms import SignUpForm, LoginForm

class LoginView(auth_views.LoginView):
    form_class = LoginForm

    def form_valid(self, form):
        remember_me = form.cleaned_data["remember_me"]
        if not remember_me:
            self.request.session.set_expiry(0)
            self.request.session.modified = True
        return super().form_valid(form)

Wir erstellen hier die angepasste LoginView, die von auth_views.LoginView``erbt. Da wir das Login-Formular ja anpassen müssen, importieren wir aus den ``.forms``die Klasse``LoginForm, obwohl es diese noch gar nicht gibt. Das angepasste Login-Formular erstellen wir in einem nächsten Schritt.

In der Methode form_valid, die nach dem Absenden des Formulars per POST-Anfrage aufgerufen wird, ziehen wir uns den Wert für das Attribut remember_me``aus den gesäuberten Daten (``form.cleaned_data). Hierbei handelt es sich um die Checkbox („Erinnere dich“), die der User vor dem Absenden anklicken kann.

Falls diese Checkbox nicht gecheckt wurde, der User also NICHT will, dass sich Django den Login „merkt“, setzen wir den Session-Cookie auf das Verfallsdatum 0 (set_expiry(0)). Damit ist der Cookie 0 Sekunden gültig. Um Django noch mitzuteilen, dass wir hier einen Session-Cookie verändert haben, müssen wir noch self.request.session.modified = True triggern.

Wurde die Checkbox vom User angeklickt, passiert einfach gar nichts. Denn das ist die Django Default Einstellung.

Das LoginForm anlegen

Nun müssen wir das LoginForm anlegen, welches wir voreilig in den event_manager/user/views.py schon importiert hatten. Dazu öffnen wir event_manager/user/forms.py und fügen folgenden Code hinzu:

# andere Imports
from django.contrib.auth.forms import AuthenticationForm

class LoginForm(AuthenticationForm):
    remember_me = forms.BooleanField(required=False, label="Erinnere dich")

Wir importieren das AuthenticationForm, dass schon alle nötigen Felder für einen User-Login enthält und erstellen ein LoginForm, welches von dem AuthenticationForm erbt. Damit enthält LoginForm alle nötigen Felder, um ein vollständiges Login-Formular zu generieren.

Darüberhinaus fügen wir jetzt noch ein boolsches Feld ein, welches frontend-seitig als Checkbox gerendert werden wird: remember_me.

Das war’s für das Formular schon.

Die URLs anpassen

Legen wir jetzt in den URLs in event_manager/user/urls.py Hand an. So sollte die Datei jetzt aussehen:

from .views import SignUpView, LoginView

   app_name = "user"

   urlpatterns = [
       path(
           "login/",
           LoginView.as_view(redirect_authenticated_user=True),
           name="login",
       ),
       path("signup/", SignUpView.as_view(), name="signup"),
   ]

Damit ist der Mechanismus fertig. Wir können das ausprobieren, in dem wir uns mal mit, mal ohne die Checkbox angeklicht zu haben einloggen und dann den Browser schließen. Bei NICHT geklickter Checkbox sollte beim Öffnen des Browsers wieder ein Login nötig sein.