Jak zakończyć sesję z powodu braku aktywności w Django?

97

Nasza aplikacja Django ma następujące wymagania dotyczące zarządzania sesjami.

  1. Sesje wygasają, gdy użytkownik zamyka przeglądarkę.
  2. Sesje wygasają po okresie bezczynności.
  3. Wykryj, kiedy sesja wygasa z powodu braku aktywności i wyświetl odpowiednią wiadomość użytkownikowi.
  4. Ostrzegaj użytkowników o zbliżającym się wygaśnięciu sesji na kilka minut przed końcem okresu bezczynności. Wraz z ostrzeżeniem należy udostępnić użytkownikom opcję przedłużenia sesji.
  5. Jeśli użytkownik pracuje nad długą działalnością biznesową w aplikacji, która nie obejmuje wysyłania żądań do serwera, sesja nie może przekraczać limitu czasu.

Po przeczytaniu dokumentacji, kodu Django i kilku związanych z tym postów na blogu, wymyśliłem następujące podejście do implementacji.

Wymaganie 1
To wymaganie można łatwo zaimplementować, ustawiając SESSION_EXPIRE_AT_BROWSER_CLOSE na True.

Wymaganie 2
Widziałem kilka zaleceń dotyczących używania SESSION_COOKIE_AGE do ustawiania okresu wygaśnięcia sesji. Ale ta metoda ma następujące problemy.

  • Sesja zawsze wygasa z końcem SESSION_COOKIE_AGE, nawet jeśli użytkownik aktywnie korzysta z aplikacji. (Można temu zapobiec, ustawiając wygaśnięcie sesji na SESSION_COOKIE_AGE dla każdego żądania przy użyciu niestandardowego oprogramowania pośredniczącego lub zapisując sesję dla każdego żądania, ustawiając SESSION_SAVE_EVERY_REQUEST na true. Jednak następny problem jest nieunikniony ze względu na użycie SESSION_COOKIE_AGE.)

  • Ze względu na sposób działania plików cookie, SESSION_EXPIRE_AT_BROWSER_CLOSE i SESSION_COOKIE_AGE wykluczają się wzajemnie, tj. Plik cookie wygasa po zamknięciu przeglądarki lub w określonym czasie wygaśnięcia. Jeśli używana jest SESSION_COOKIE_AGE, a użytkownik zamyka przeglądarkę przed wygaśnięciem pliku cookie, plik cookie zostaje zachowany, a ponowne otwarcie przeglądarki umożliwi użytkownikowi (lub komukolwiek innemu) dostęp do systemu bez ponownego uwierzytelniania.

  • Django opiera się tylko na obecnym pliku cookie, aby określić, czy sesja jest aktywna. Nie sprawdza daty wygaśnięcia sesji przechowywanej w sesji.

Aby zaimplementować to wymaganie i obejść powyższe problemy, można zastosować następującą metodę.

  • Nie ustawiaj SESSION_COOKIE_AGE.
  • Ustaw datę wygaśnięcia sesji na „bieżący czas + okres bezczynności” dla każdego żądania.
  • Zastąp process_request w SessionMiddleware i sprawdź wygaśnięcie sesji. Odrzuć sesję, jeśli wygasła.

Wymaganie 3
Gdy wykryjemy, że sesja wygasła (w niestandardowym SessionMiddleware powyżej), ustaw atrybut w żądaniu, aby wskazać wygaśnięcie sesji. Atrybut ten może służyć do wyświetlania użytkownikowi odpowiedniego komunikatu.

Wymaganie 4
Użyj JavaScript, aby wykryć brak aktywności użytkownika, przekazać ostrzeżenie, a także opcję przedłużenia sesji. Jeśli użytkownik chce przedłużyć sesję, wyślij do serwera impuls podtrzymujący, aby przedłużyć sesję.

Wymaganie 5
Użyj JavaScript do wykrywania aktywności użytkownika (podczas długich operacji biznesowych) i wysyłaj impulsy podtrzymujące do serwera, aby zapobiec wygaśnięciu sesji.


Powyższe podejście do implementacji wydaje się bardzo skomplikowane i zastanawiałem się, czy nie ma prostszej metody (szczególnie dla Wymagania 2).

Wszelkie spostrzeżenia będą bardzo mile widziane.

Akbar ibrahim
źródło
3
+1 za dostarczenie szczegółowego rozwiązania
Don
Istnieje oprogramowanie pośredniczące, które może zrobić to, czego potrzebujesz. na github i na
pypi
1
„Ze względu na sposób działania plików cookie, SESSION_EXPIRE_AT_BROWSER_CLOSE i SESSION_COOKIE_AGE wykluczają się wzajemnie, tj. Plik cookie wygasa po zamknięciu przeglądarki lub w określonym czasie. przeglądarka pozwoli użytkownikowi (lub komukolwiek innemu) na dostęp do systemu bez ponownego uwierzytelniania ”. Popraw mnie, jeśli się mylę, ale wydaje się, że nie jest to już prawdą w nowszych wersjach Django? (Co najmniej 1,5+)
Botond Béres
1
„Django opiera się tylko na obecnym pliku cookie w celu określenia, czy sesja jest aktywna. Nie sprawdza daty wygaśnięcia sesji przechowywanej w sesji”. To już nie jest prawdą .
knaperek

Odpowiedzi:

44

Oto pomysł ... Wygaś sesję po zamknięciu przeglądarki z SESSION_EXPIRE_AT_BROWSER_CLOSEustawieniem. Następnie ustaw znacznik czasu w sesji dla każdego takiego żądania.

request.session['last_activity'] = datetime.now()

i dodaj oprogramowanie pośredniczące, aby wykryć, czy sesja wygasła. coś takiego powinno obsłużyć cały proces ...

from datetime import datetime
from django.http import HttpResponseRedirect

class SessionExpiredMiddleware:
    def process_request(request):
        last_activity = request.session['last_activity']
        now = datetime.now()

        if (now - last_activity).minutes > 10:
            # Do logout / expire session
            # and then...
            return HttpResponseRedirect("LOGIN_PAGE_URL")

        if not request.is_ajax():
            # don't set this for ajax requests or else your
            # expired session checks will keep the session from
            # expiring :)
            request.session['last_activity'] = now

Następnie wystarczy utworzyć adresy URL i widoki, aby zwrócić odpowiednie dane do wywołań Ajax dotyczących wygaśnięcia sesji.

gdy użytkownik zdecyduje się „odnowić” sesję, że tak powiem, wszystko, co musisz zrobić, to ponownie ustawić requeset.session['last_activity']aktualny czas

Oczywiście ten kod to dopiero początek ... ale powinien poprowadzić Cię na właściwą ścieżkę

Jiaaro
źródło
Jestem tutaj sceptyczny, ale nie sądzę, że if not request.is_ajax()jest to całkowicie bezpieczne. Czy ktoś, kto przechwyci sesję przed wygaśnięciem, może sfałszować / wysłać połączenie AJAX i podtrzymać sesję?
notbad.jpeg
2
@ notbad.jpeg: ogólnie rzecz biorąc, „aktywność” jest łatwa do sfałszowania. Ktoś, kto przechwytuje sesję i nadal wysyła żądania, jest po prostu aktywny.
RemcoGerlich
To świetna odpowiedź. Oprogramowanie pośredniczące jest bardzo słabo wykorzystywanym narzędziem w rozwoju Django.
Jamie Counsell
34

Jestem całkiem nowy w używaniu Django.

Chciałem, aby sesja wygasła, jeśli zalogowany użytkownik zamknie przeglądarkę lub jest w stanie bezczynności (limit czasu nieaktywności) przez pewien czas. Kiedy wyszukałem to w Google, aby dowiedzieć się, najpierw pojawiło się to pytanie SOF. Dzięki ładnej odpowiedzi wyszukałem zasoby, aby zrozumieć, jak działa oprogramowanie pośredniczące podczas cyklu żądanie / odpowiedź w Django. To było bardzo pomocne.

Miałem właśnie zastosować niestandardowe oprogramowanie pośredniczące w moim kodzie, postępując zgodnie z najlepszą odpowiedzią tutaj. Ale nadal byłem trochę podejrzliwy, ponieważ najlepsza odpowiedź tutaj została zredagowana w 2011 roku. Poświęciłem więcej czasu na przeszukiwanie ostatnich wyników wyszukiwania i wymyśliłem prosty sposób.

SESSION_EXPIRE_AT_BROWSER_CLOSE = True
SESSION_COOKIE_AGE = 10 # set just 10 seconds to test
SESSION_SAVE_EVERY_REQUEST = True

Nie sprawdzałem innych przeglądarek poza chrome. 1. Sesja wygasła, kiedy zamknąłem przeglądarkę, nawet jeśli ustawiono SESSION_COOKIE_AGE. 2. Tylko gdy byłem bezczynny przez ponad 10 sekund, sesja wygasła. Dzięki SESSION_SAVE_EVERY_REQUEST, za każdym razem, gdy pojawi się nowe żądanie, zapisuje sesję i aktualizuje limit czasu do wygaśnięcia

Aby zmienić to domyślne zachowanie, ustaw SESSION_SAVE_EVERY_REQUEST na True. Gdy ustawione na True, Django zapisze sesję w bazie danych przy każdym żądaniu.

Należy pamiętać, że plik cookie sesji jest wysyłany tylko wtedy, gdy sesja została utworzona lub zmodyfikowana. Jeśli SESSION_SAVE_EVERY_REQUEST ma wartość True, plik cookie sesji będzie wysyłany przy każdym żądaniu.

Podobnie, część wygasłego pliku cookie sesji jest aktualizowana za każdym razem, gdy wysyłany jest plik cookie sesji.

Podręcznik django 1.10

Po prostu zostawiam odpowiedź, aby niektórzy ludzie, którzy są nowicjuszami w Django, tacy jak ja, nie spędzali zbyt wiele czasu na szukaniu rozwiązania, tak jak ja.

Jayground
źródło
26

django-session-security robi właśnie to ...

... z dodatkowym wymaganiem: jeśli serwer nie odpowiada lub osoba atakująca rozłączyła połączenie internetowe: i tak powinno wygasnąć.

Disclamer: utrzymuję tę aplikację. Ale oglądam ten wątek od bardzo, bardzo długiego czasu :)

jpic
źródło
1
fajna aplikacja - ładnie zaprojektowana i dobrze zbudowana. ładny, czysty kod ... dziękuję.
nicorellius
Jeśli użytkownik zamyka przeglądarkę lub zakładkę (bez wylogowania), kiedy opuszcza, czy nadal zmusza to użytkownika do wylogowania? Czy radzi sobie z tym stanem?
Mehmet Kagan Kayaalp
Byłoby to obsługiwane przez wygasanie sesyjnego pliku cookie czystego http, prawda?
jpic
11

Prostym sposobem spełnienia drugiego wymagania byłoby ustawienie wartości SESSION_COOKIE_AGE w settings.py na odpowiednią liczbę sekund. Na przykład:

SESSION_COOKIE_AGE = 600      #10 minutes.

Jednak tylko w ten sposób sesja wygaśnie po 10 minutach, niezależnie od tego, czy użytkownik wykaże jakąś aktywność. Aby poradzić sobie z tym problemem, czas wygaśnięcia może być automatycznie przedłużany (o kolejne dodatkowe 10 minut) za każdym razem, gdy użytkownik wykona dowolny rodzaj żądania z następującym zdaniem:

request.session.set_expiry(request.session.get_expiry_age())
Fernando Martin
źródło
2
SESSION_COOKIE_AGE = 600 Spowoduje to wydłużenie wieku sesji przy każdym nowym żądaniu lub odświeżeniu strony
Aseem
1
Potwierdzam, że samo ustawienie SESSION_COOKIE_AGEwystarczy i że każde żądanie (wysłanie sesyjnego pliku cookie) automatycznie odświeży datę wygaśnięcia sesji.
bruno desthuilliers
3

możesz również użyć funkcji wbudowanych w stackoverflow

SESSION_SAVE_EVERY_REQUEST = True
mexekanez
źródło
Całkowicie niezwiązane - służy do wymuszania zapisu sesji (nie „odświeżania pliku cookie sesji”) dla każdego żądania bez sprawdzania, czy sesja została zmodyfikowana.
bruno desthuilliers
3

W pierwszym żądaniu możesz ustawić wygaśnięcie sesji jako

self.request.session['access_key'] = access_key
self.request.session['access_token'] = access_token
self.request.session.set_expiry(set_age) #in seconds 

Korzystając z klucza dostępu i tokena,

try:
    key = self.request.session['access_key']
except KeyError:
    age = self.request.session.get_expiry_age()
    if age > set_age:
        #redirect to login page
tilaprimera
źródło