Разрешение только одного активного сеанса для каждого пользователя в приложении Django
Я хочу ограничить зарегистрированных пользователей только одним активным сеансом, т.е. если пользователь входит в систему с новым сеансом, старый сеанс должен быть завершен.
Я нашел много помощи на SO уже:
здесь и здесь
Я реализовал промежуточное решение, с немного дополнительной проверкой...
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
До сих пор, так хорошо... на сервере Django dev (manage.py runningerver) все работает правильно, он запускает старый сеанс...
... но при использовании Apache (с mod_wsgi) он не работает!
Я пытался найти информацию об этом, но пока не повезло...
Самое близкое, что я нашел, это this, но это своего рода "противоположная" проблема...
Любая помощь будет высоко оценена.
Изменить: я добавил отладочную печать перед удалением сеанса...
здесь фрагмент из Apache error.log:
[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
первые две ложь - это когда я вступил с первой сессией (Firefox)
последние два относятся к тому, когда я вошел со вторым сеансом (Chromium)
... получается, что старая запись сеанса не удаляется...???
Я работаю против одного и того же экземпляра PostgreSQL, как и с devserver...
Edit2: выяснилось, что мой код был ошибкой... он не удался, когда новый Session_key не был найден в сеансе...
здесь фиксированный код... try..except теперь находится в правильном месте
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()
Ответы
Ответ 1
Вы всегда можете использовать этот подход, хотя не рекомендуется, он работает.
my_old_sessions = Session.objects.all()
for row in my_old_sessions:
if row.get_decoded().get("_username") == request.user.username:
row.delete()
Вы выполнили бы код выше в своей функции login() прямо перед аутентификацией пользователя.
Это, конечно, работает только если у вас есть метод функции login(), который хранит имя пользователя USERS в его сеансе, как показано ниже:
request.session["_username"] = request.user.username
Если вы используете этот подход, просто запомните, что вы запустили свою базу данных всех своих сеансов перед запуском своего сервера после внесения этих изменений, потому что это приведет к возникновению ошибок KeyLookUp.
Ответ 2
На самом деле действительно много подобных вопросов, но вот мое решение.
Когда пользователь входит в систему, пройдите все активные сеансы и удалите те же user.id
. Для небольших сайтов это должно быть хорошо.
# __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)
Ответ 3
Я чувствую, что, как-то, сигналы django.contrib.auth могут помочь здесь. При входе в систему аннулировать старые пользовательские сеансы.