Может понадобиться для какого-нибудь портала в интранете или чего-то такого секюрного.
Решается всё довольно банально, есть специальный сигнал
user_logged_in.Ищем сессию для этого юзера, если нашли - сбрасываем в базе, сбрасываем в текущем engine (если это применимо).
Если что, у меня настроен один из стандартных:
SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'Код обработчика сигнала. Всё заполонил комментариями, думаю, там всё понятно.
from django.contrib.auth.signals import user_logged_in
from django.contrib import messages
from django.contrib.sessions.models import Session
from django.utils import timezone
def user_logged_in_handler(sender, request, user, **kwargs):
# текущая сессия: request.session.session_key
for session in Session.objects.filter(expire_date__gte=timezone.now()).exclude(session_key=request.session.session_key):
if session.get_decoded().get('_auth_user_id', None) == str(user.id):
# нашли какую-то сессию, делаем её expired
session.expire_date = timezone.now()
session.save()
# далее нам надо очистить все данные сессии
# сначала я пробовал так
# SessionStore = session.get_session_store_class()
# sesstor = SessionStore(session.session_key)
# sesstor.flush()
# где flush - это метод именно для моего бекенда cached_db
# я ожидал, что для модели вернётся текущий настроенный в settings.SESSION_ENGINE бекенд, но в модели Session захардкожено (как ни странно?) db.SessionStore
# в принципе можно напрямую тупо импортировать cached_db.SessionStore, но сделал проще и универсальнее:
# request.session у нас уже содержит установленный мидлварей нужный SessionStore
# с помощью "чужой" нашей новой SessionStore удаляем левый наш старый session_key, там удачно есть такой даже метод
request.session.delete(session.session_key)
# раз у нас доступен request, то при желании показываем юзеру сообщение
messages.add_message(request, messages.INFO, 'Была закрыта дублирующаяся сессия на каком-то другом компьютере')
user_logged_in.connect(user_logged_in_handler)