Как найти сеанс django для конкретного пользователя?

Я пишу приложение, в котором я буду получать доступ к базе данных из django и из автономного приложения. Оба должны выполнить проверку сеанса, и сеанс должен быть одинаковым для них обоих. Django имеет встроенную аутентификацию / проверку сеанса, которую я использую, теперь мне нужно выяснить, как повторно использовать тот же сеанс для моего автономного приложения.

У меня вопрос: как мне найти session_key для конкретного пользователя?

Судя по всему, нет ничего, что связывало бы auth_user и django_session

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
55
0
36 319
6
Перейти к ответу Данный вопрос помечен как решенный

Ответы 6

Это несколько сложно сделать, потому что не каждый сеанс обязательно связан с аутентифицированным пользователем; Фреймворк сеансов Django также поддерживает анонимные сеансы, и любой, кто посещает ваш сайт, будет иметь сеанс, независимо от того, вошел ли он в систему.

Это еще больше усложняется тем фактом, что сериализуется сам объект сеанса - поскольку Django не имеет возможности узнать, какие именно данные вы хотите сохранить, он просто сериализует словарь данных сеанса в строку (используя стандартный Python "pickle" модуль) и вставляет его в вашу базу данных.

Если у вас есть ключ сеанса (который будет отправлен браузером пользователя как значение cookie "sessionid"), самый простой способ получить данные - просто запросить таблицу сеанса для сеанса с этим ключом, который возвращает сеанс объект. Затем вы можете вызвать метод этого объекта get_decoded (), чтобы получить словарь данных сеанса. Если вы не используете Django, вы можете посмотреть исходный код (django / contrib / sessions / models.py), чтобы увидеть, как десериализуются данные сеанса.

Однако, если у вас есть идентификатор пользователя, вам нужно будет пройти через все объекты сеанса, десериализуя каждый из них и ища тот, который имеет ключ с именем «_auth_user_id» и для которого значением этого ключа является идентификатор пользователя. .

и почему бы не привязать сеанс к пользователю в промежуточном программном обеспечении, а не к запросу, как во встроенной версии?

FlogFR 05.12.2014 12:14
Ответ принят как подходящий

Изменение таблицы django_session для добавления явного user_id может значительно облегчить жизнь. Предполагая, что вы это сделаете (или что-то подобное), вот четыре подхода к выбору вещей по своему вкусу:

Разветвите код django.contrib.session. Я знаю, знаю, это ужасно предлагать. Но это всего 500 строк, включая все бэкенды без тестов. Взломать довольно просто. Это лучший маршрут только в том случае, если вы собираетесь серьезно переставить вещи.

Если вы не хотите форкнуть, вы можете попробовать подключиться к сигналу Session.post_save и сыграть там.

Или вы можете MonkeyPatch contrib.session.models.Session.save(). Просто оберните существующий метод (или создайте новый), выделите / синтезируйте любые значения, которые вам нужны, сохраните их в своих новых полях, а затем super(Session, self).save().

Еще один способ сделать это - добавить 2 (да, два) класса промежуточного программного обеспечения - один до и один после SessionMiddleware в вашем файле settings.py. Это связано со способом обработки промежуточного программного обеспечения. Тот, который указан после SessionMiddleware, получит во входящем запросе запрос с уже присоединенным к нему сеансом. Один из перечисленных выше может выполнять любую обработку ответа и / или изменять / повторно сохранять сеанс.

Мы использовали вариант этого последнего метода для создания псевдосеансов для пауков поисковых систем, чтобы предоставить им особый доступ к материалам, которые обычно доступны только участникам. Мы также обнаруживаем входящие ссылки, в которых поле REFERER находится в соответствующей поисковой системе, и даем пользователю полный доступ к этой статье.

Обновлять:

Мой ответ теперь довольно древний, хотя в основном он верен. См. Гораздо более свежий ответ @ Gavin_Ballard (29 сентября 2014 г.) ниже, чтобы узнать о еще одном подходе к этой проблеме.

Это может нарушить тесты, если вы сделаете это без разветвления кода django. Если приложение сеансов находится в INSTALLED_APPS, таблица сеансов будет создана дважды, что приведет к прерыванию тестов. Я не уверен, безопасно ли удалять приложение сеанса django, если вы предоставили свою собственную таблицу django_session.

Stephen Paulger 25.05.2012 20:05

Теперь доступны пользовательские сеансы Django, которые предоставляют вам эту функциональность: github.com/Bouke/django-user-sessions

sww314 28.02.2015 00:33

@ sww314 - Я быстро просмотрел связанный проект GitHub, и это выглядит как очень разумный подход к проблеме. Спасибо!

Peter Rowell 28.02.2015 00:39

Питер Роуэлл, спасибо за ответ. Это была огромная помощь. Вот что я сделал, чтобы он заработал. Пришлось изменить только один файл в djang.contrib.sessions.

В django / contrib / sessions / models.py добавьте user_id в таблицу (добавьте в таблицу БД вручную или удалите таблицу и запустите manage.py syncdb).

class Session(models.Model):

    ...

    user_id = models.IntegerField(_('user_id'), null=True)

    ...

    def save(self, *args, **kwargs):
        user_id = self.get_decoded().get('_auth_user_id')
        if ( user_id != None ):
            self.user_id = user_id

        # Call the "real" save() method.
        super(Session, self).save(*args, **kwargs)

Теперь в вашем представлении, где вы выполняете вход (если вы используете базовый вход в django, вам придется его переопределить)

# On login, destroy all prev sessions
        # This disallows multiple logins from different browsers
        dbSessions = Session.objects.filter( user_id = request.user.id )
        for index, dbSession in enumerate( dbSessions ):
            if ( dbSession.session_key != request.session.session_key ):
                dbSession.delete()

Это сработало для меня.

Я нашел этот фрагмент кода

from django.contrib.sessions.models import Session
from django.contrib.auth.models import User

session_key = '8cae76c505f15432b48c8292a7dd0e54'

session = Session.objects.get(session_key=session_key)
uid = session.get_decoded().get('_auth_user_id')
user = User.objects.get(pk=uid)

print user.username, user.get_full_name(), user.email

здесь http://scottbarnham.com/blog/2008/12/04/get-user-from-session-key-in-django/

Еще не проверял, но выглядит довольно прямолинейно.

Прямая, но обратная операция. Получение ключа из идентификатора пользователя (без изменения моделей) требует повторения каждого сеанса.

kibibu 17.08.2012 09:57

Голосование за поддержку, потому что поиск Google по запросу "сеанс десериализации django" ведет сюда.

Serge Rogatch 13.01.2019 15:38

Я столкнулся с этой проблемой, когда хотел выгнать спамера. Кажется, что сделать их учетную запись «неактивной» недостаточно, потому что они все еще могут войти в свой предыдущий сеанс. Итак - как удалить сеанс для определенного пользователя или как сознательно истечь сеанс для определенного пользователя?

Ответ заключается в том, чтобы использовать поле last_login для отслеживания времени, когда сеанс был отключен, что говорит вам, что expire_date - две недели спустя, что позволяет вам выполнить полезный фильтр для таблицы сеансов:

from django.contrib.sessions.models import Session
from django.contrib.auth.models import User
from datetime import datetime
from dateutil.relativedelta import relativedelta

baduser = User.objects.get(username = "whoever")     
two_weeks = relativedelta(weeks=2)
two_hours = relativedelta(hours=2)
expiry = baduser.last_login + two_weeks
sessions = Session.objects.filter(
    expire_date__gt=expiry - two_hours,
    expire_date__lt=expiry + two_hours
) 
print sessions.count() # hopefully a manageable number

for s in sessions:
    if s.get_decoded().get('_auth_user_id') == baduser.id:
        print(s)
        s.delete()

Хакерский, но эффективный, мне это нравится! К сожалению, я думаю, что это не работает, когда серверная часть сеанса - это redis, а не БД.

Nick Sweeting 17.09.2016 00:38

Этот ответ публикуется через пять лет после исходного вопроса, но эта ветка SO является одним из лучших результатов Google при поиске решения этой проблемы (и это все еще то, что не поддерживается Django из коробки).

У меня есть альтернативное решение для случая использования, когда вас интересуют только зарегистрированные пользовательские сеансы, в котором используется дополнительная модель UserSession для сопоставления пользователей с их сеансами, примерно так:

from django.conf import settings
from django.db import models
from django.contrib.sessions.models import Session

class UserSession(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL)
    session = models.ForeignKey(Session)  

Затем вы можете просто сохранить новый экземпляр UserSession каждый раз, когда пользователь входит в систему:

from django.contrib.auth.signals import user_logged_in

def user_logged_in_handler(sender, request, user, **kwargs):
    UserSession.objects.get_or_create(user = user, session_id = request.session.session_key)

user_logged_in.connect(user_logged_in_handler)

И, наконец, когда вы хотите перечислить (и, возможно, очистить) сеансы для конкретного пользователя:

from .models import UserSession

def delete_user_sessions(user):
    user_sessions = UserSession.objects.filter(user = user)
    for user_session in user_sessions:
        user_session.session.delete()

Это все гайки и болты, если вы хотите более подробно, у меня есть Сообщение блога, покрывающий это.

Проблема, с которой я столкнулся, заключается в том, что сеанс не обязательно сохраняется при срабатывании user_logged_in, и поэтому возникает ошибка ограничения FK. Безопасно ли сначала запускать request.session.save() в user_logged_in_handler()? В качестве альтернативы я мог бы изменить поле FK на текст, но это означает, что мне, вероятно, потребуется дополнительная очистка.

DanH 31.08.2015 13:09

Дополнительный вопрос: что, если бы я хотел отслеживать логины пользователей? Удалив сеанс, я потеряю это. Как я могу аннулировать сеанс, не удаляя его?

alfetopito 31.08.2015 22:11

@DanH Я не видел этой проблемы; вместо того, чтобы звонить request.session.save(), я бы выбрал второй вариант и не использовал бы FK. Единственная дополнительная очистка, которую вам нужно сделать, - это вызвать user_session.delete() после user_session.session.delete(). (В этом примере вам нужно будет добавить метод session() к вашей модели UserSession, чтобы найти правильный сеанс.)

Gavin Ballard 01.09.2015 13:30

@alfetopito: Вы можете просто изменить отношение FK модели UserSession, чтобы не было делецией CASCADE. Затем вы будете вести учет входов в систему в таблице UserSession, по-прежнему делая недействительными сами сеансы.

Gavin Ballard 01.09.2015 13:31

@GavinBallard спасибо! В итоге я использовал что-то похожее на то, что вы сказали. Я сохранил session_key в CharField, а не как ForeignKey, в основном, чтобы избежать исключений целостности БД, поскольку объект сеанса сохраняется только после возврата из представления. Кроме того, таким образом я могу удалить объект сеанса без каскадного эффекта.

alfetopito 03.09.2015 21:47

Я столкнулся с той же проблемой FK, что и @DanH, что кажется логичным решением. Удаление внешнего ключа кажется недействительным в остальном элегантное решение.

Jesse 16.12.2015 20:30

Если я чего-то не упускаю, еще одна проблема с этим подходом заключается в том, что если у вас очень короткие сеансы (скажем, 30 минут или один час), он не поймает тех пользователей, сеансы которых истекли (поскольку у них не было возможности выйти). Короткие сеансы необходимы, если у вас есть веб-сайт, который позволяет пользователю видеть, какие другие пользователи вошли в систему.

Jim 19.12.2015 20:51

@ Роберт: Я не совсем понимаю, что вы имеете в виду. Даже если срок сеанса истек, он все равно будет сопоставлен пользователю через модель UserSession. Если у вас есть что-то для очистки истекших пользовательских сеансов (например, задание cron), тогда да, отображение UserSession также будет удалено - но это обычно желательное поведение. Основная цель этого - удалить все оставшиеся допустимые сеансы для пользователя.

Gavin Ballard 21.12.2015 18:36

/ admin / дает следующую ошибку ... user_logged_in_handler () принимает ровно 3 аргумента (2 задано) в параметрах, которые я заменил user на CustomUser, поскольку у меня есть расширенные модели пользователей с помощью AbstractUser.

Shefali 21.09.2017 20:11

Другие вопросы по теме