.. _remember_me: .. index:: single: Erinnere dich single: login single: Session-Cookie 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. .. code-block:: python 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: .. code-block:: python 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: .. code-block:: python # 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: .. code-block:: python 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.