2012-01-19 11 views
21

Chcę ograniczyć zalogowanych użytkowników, aby mieli tylko jedną aktywną sesję, tj. Jeśli użytkownik zaloguje się z nowym identyfikatorem sesji, stara sesja powinna zostać zakończona. znalazłem dużo pomocy na SO już: here i hereZezwalanie tylko na jedną aktywną sesję na użytkownika w aplikacji Django

I wdrożone rozwiązanie middleware, z odrobiną dodatkowego sprawdzanie ...

class OnlyOneUserMiddleware(object): 
""" 
Middleware to ensure that a logged-in user only has one session active. 
Will kick out any previous session. 
""" 
def process_request(self, request): 
    if request.user.is_authenticated(): 
     try: 
      cur_session_key = request.user.get_profile().session_key 
      if cur_session_key and cur_session_key != request.session.session_key: 
       # Default handling... kick the old session... 
       Session.objects.get(session_key=cur_session_key).delete() 
      if not cur_session_key or cur_session_key != request.session.session_key: 
       p = request.user.get_profile() 
       p.session_key = request.session.session_key 
       p.save() 
     except ObjectDoesNotExist: 
      pass 

Tak daleko, tak dobrze ... na serwer dev Django (serwer zarządzający manage.py) wszystko działa poprawnie, kopie starą sesję ...

... ale przy użyciu Apache (z mod_wsgi), to nie działa!

Starałem się znaleźć żadnych informacji na ten temat, ale nie ma szczęścia do tej pory ...

Najbliższy znalazłem jest this, ale to niby „przeciwnym” problem ...

Każda pomoc będzie bardzo ceniona.

Edit: dodałem wydruk debugowania przed usunięciem Session ... oto fragment z error.log Apache:

[Fri Jan 20 09:56:50 2012] [error] old key = f42885ccb7f33b6afcb2c18fca14f44a 
[Fri Jan 20 09:56:50 2012] [error] new key = ce4cfb672e6025edb8ffcd0cf2b4b8d1 
[Fri Jan 20 09:57:14 2012] [error] old key = f42885ccb7f33b6afcb2c18fca14f44a 
[Fri Jan 20 09:57:14 2012] [error] new key = 0815c56241ac21cf4b14b326f0aa7e24 

pierwsze dwa kłamstwa są od kiedy wszedł do pierwszej sesji (Firefox)

dwa ostatnie są od kiedy wszedłem z drugiej sesji (chrom)

... okazuje się, że stary zapis sesji nie zostaną usunięte ... ???

biegnę w porównaniu z dokładnie tej samej instancji PostgreSQL jak ja z devserver ...

Edit2: Okazało się, że mój kod był buggy ... to nie udało, gdy nowy Session_key nie było znalezione w sesji ...

oto stały kod ... The try..except jest teraz w odpowiednim miejscu

class OnlyOneUserMiddleware(object): 
    """ 
    Middleware to ensure that a logged-in user only has one session active. 
    Will kick out any previous session. 
    """ 
    def process_request(self, request): 
     if request.user.is_authenticated(): 
      cur_session_key = request.user.get_profile().session_key 
      if cur_session_key and cur_session_key != request.session.session_key: 
       # Default handling... kick the old session... 
       try: 
        s = Session.objects.get(session_key=cur_session_key) 
        s.delete() 
       except ObjectDoesNotExist: 
        pass 
      if not cur_session_key or cur_session_key != request.session.session_key: 
       p = request.user.get_profile() 
       p.session_key = request.session.session_key 
       p.save() 
+4

Kiedy mówisz "nie działa", co dokładnie nie działa? Wciąż widzisz starą sesję w DB? Jeśli umieścisz wywołanie drukowania/rejestrowania tuż przed usunięciem 'Session', czy widzisz, że zostało wykonane w' mod_wsgi'? – AdamKG

+0

@AdamKG: Dzięki za wskazanie mnie we właściwym kierunku! –

+0

Jeśli silnik sesji to cache_db, myślę, że musimy również ręcznie usunąć sessionkey z pamięci podręcznej, prawda? – Wesley

Odpowiedz

1

zawsze można użyć tego podejścia, choć nie zalecane, to działa.

my_old_sessions = Session.objects.all() 
for row in my_old_sessions: 
    if row.get_decoded().get("_username") == request.user.username: 
     row.delete() 

Zaimplementujesz powyższy kod w swojej funkcji login() tuż przed uwierzytelnieniem użytkownika.

To oczywiście działa tylko jeśli masz login() metoda function, która przechowuje Użytkownik w swojej sesji jak następuje:

request.session["_username"] = request.user.username 

Jeśli używasz tego podejścia tylko pamiętać, aby wyczyścić bazę danych wszystkich sesje przed uruchomieniem serwera po wprowadzeniu tych zmian, ponieważ spowoduje to zwiększenie błędów KeyLookUp.

+0

_username nie jest już w obiekcie get_decoded() obiektu (Django 1.8). Jednak user_id jest, więc 'row.get_decoded(). Get (" _ auth_user_id ") == request.user.pk" będzie działać. – guival

1

Jest wiele podobnych pytań w każdym miejscu, ale oto moje rozwiązanie.

Po zalogowaniu użytkownik przechodzi przez wszystkie aktywne sesje i usuwa te z tym samym user.id.W przypadku mniejszych witryn powinno to wyglądać dobrze.

# __init__.py 
# Logs user out from all other sessions on login, django 1.8 

from django.contrib.sessions.models import Session 
from django.contrib.auth.signals import user_logged_in 
from django.db.models import Q 
from django.utils import timezone 

def limit_sessions(sender, user, request, **kwargs): 
    # this will be slow for sites with LOTS of active users 

    for session in Session.objects.filter(
     ~Q(session_key = request.session.session_key), 
     expire_date__gte = timezone.now() 
    ): 
     data = session.get_decoded() 
     if data.get('_auth_user_id', None) == str(user.id): 
      # found duplicate session, expire it 
      session.expire_date = timezone.now() 
      session.save() 

    return 

user_logged_in.connect(limit_sessions) 
+0

Czy porównałeś wydajność między dwoma sposobami? Mam na myśli sposób przez sygnały i oprogramowanie pośrednie. Jeśli używasz oprogramowania pośredniego, wszystkie żądania powinny przejść przez handle_request. Jeśli sygnały użycia, chociaż tylko zdarzenie logowania uderzy w funkcję, ale będzie się powtarzać nad tabelą sesji ... więc pomyśl o wydajności, która jest lepsza ... – Wesley

+0

Powiedziałem "dla mniejszych witryn" :) - możesz chcieć przechowuj klucz taki jak OP, ale sprawdzaj tylko przy logowaniu, tak jak ja, dla bardziej wydajnego rozwiązania. – MarZab

+0

Tak, widzę, że wspomniałeś o mniejszych stronach :-) Po prostu chcę wiedzieć, co jest lepsze dla większych witryn :-) – Wesley

0

Czuję, że w jakiś sposób, django.contrib.auth sygnały mogą pomóc tutaj. Podczas logowania unieważniaj starsze sesje użytkowników.

+0

Jeśli jesteś już pracuje z bazą danych użytkowników, oznaczałoby to, że wszyscy użytkownicy musieliby być najpierw wylogowani, aby to zadziałało. – guival

Powiązane problemy