Как принудительно выйти из системы в django?

В моем приложении django при определенных условиях мне нужно заставить пользователя выйти из системы по имени пользователя. Не обязательно текущий пользователь, который вошел в систему, но другой пользователь. Таким образом, метод запроса в моем представлении не имеет информации о сеансе пользователя, который я хочу выйти из системы.

Я знаком с django.auth и с методом auth.logout, но он принимает запрос в качестве аргумента. Есть ли способ «django-way» для регистрации пользователя, если у меня есть имя пользователя? Или мне нужно свернуть собственный logout sql?

Я не думаю, что в Django есть санкционированный способ сделать это.

Идентификатор пользователя сохраняется в объекте сеанса, но он кодируется. К сожалению, это означает, что вам придется проходить через все сеансы, декодировать и сравнивать …

Два шага:

Сначала удалите объекты сеанса для целевого пользователя. Если они войдут с нескольких компьютеров, у них будет несколько объектов сеанса.

from django.contrib.sessions.models import Session from django.contrib.auth.models import User # grab the user in question user = User.objects.get(username='johndoe') [s.delete() for s in Session.objects.all() if s.get_decoded().get('_auth_user_id') == user.id] 

Тогда, если вам нужно, запереть их.

 user.is_active = False user.save() 

Хотя ответ Гарольда работает в этом конкретном случае, я вижу по крайней мере две важные проблемы:

  1. Это решение может использоваться только с движком сеанса базы данных. В других ситуациях (кеш, файл, cookie) модель Session не будет использоваться.
  2. Когда количество сеансов и пользователей в базе данных растет, это становится довольно неэффективным.

Чтобы решить эти проблемы, я предлагаю вам воспользоваться другим подходом к проблеме. Идея состоит в том, чтобы хранить где-то дату, когда пользователь вошел в систему для данного сеанса, и в последний раз, когда вы попросили пользователя выйти из системы.

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

Теперь давайте посмотрим на возможную реализацию этого решения для django 1.3b1 . В три этапа:

1. сохранить в сеансе последнюю дату входа в систему

К счастью, система Django auth предоставляет сигнал с именем user_logged_in . Вам просто нужно зарегистрировать эти сигналы и сохранить текущую дату в сеансе. В нижней части your models.py :

 from django.contrib.auth.signals import user_logged_in from datetime import datetime def update_session_last_login(sender, user=user, request=request, **kwargs): if request: request.session['LAST_LOGIN_DATE'] = datetime.now() user_logged_in.connect(update_session_last_login) 

2. попросите выйти из системы для пользователя

Нам просто нужно добавить поле и метод в модель User . Существует несколько способов достижения этого ( профили пользователей , наследование модели и т. Д.), Каждый из которых имеет плюсы и минусы.

Для простоты я собираюсь использовать наследование модели здесь, если вы идете на это решение, не забудьте написать собственный сервер аутентификации .

 from django.contrib.auth.models import User from django.db import models from datetime import datetime class MyUser(User): force_logout_date = models.DateTimeField(null=True, blank=True) def force_logout(self): self.force_logout_date = datetime.now() self.save() 

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

 from myapp.models import MyUser MyUser.objects.get(username='johndoe').force_logout() 

3. выполнить проверку доступа

Лучшим способом здесь является использование промежуточного программного обеспечения в качестве предлагаемого. Это промежуточное программное обеспечение будет обращаться к request.user , поэтому вам нужно поместить его после 'django.contrib.auth.middleware.AuthenticationMiddleware' в настройках MIDDLEWARE_CLASSES .

 from django.contrib.auth import logout class ForceLogoutMiddleware(object): def process_request(self, request): if request.user.is_authenticated() and request.user.force_logout_date and \ request.session['LAST_LOGIN_DATE'] < request.user.force_logout_date: logout(request) 

Это должно сделать это.


Заметки

  • Помните о влиянии на производительность хранения дополнительного поля для ваших пользователей. Использование наследования модели добавит дополнительный JOIN . Использование профилей пользователей добавит дополнительный запрос. Изменение непосредственно User - это лучший способ работы, но это все еще волосатая тема .
  • Если вы разместите это решение на существующем сайте, у вас, вероятно, возникнут проблемы с существующими сеансами, в которых не будет ключа 'LAST_LOGIN_DATE' . Вы можете немного адаптировать код промежуточного кода для решения этого случая:

     from django.contrib.auth import logout class ForceLogoutMiddleware(object): def process_request(self, request): if request.user.is_authenticated() and request.user.force_logout_date and \ ( 'LAST_LOGIN_DATE' not in request.session or \ request.session['LAST_LOGIN_DATE'] < request.user.force_logout_date ): logout(request) 
  • В django 1.2.x отсутствует сигнал user_logged_in . Вернитесь к переопределению функции login :

     from django.contrib.auth import login as dj_login from datetime import datetime def login(request, user): dj_login(request, user) request.session['LAST_LOGIN_DATE'] = datetime.now() 

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

 from django.contrib.auth import logout class ActiveUserMiddleware(object): def process_request(self, request): if not request.user.is_authenticated(): return if not request.user.is_active: logout(request) 

Просто добавьте это промежуточное программное обеспечение в свои настройки и от вас. В случае смены паролей вы можете ввести новое поле в модели userprofile, которое заставляет пользователя выйти из системы, проверить значение поля вместо is_active выше, а также отключить поле при входе пользователя в систему. Последнее может выполняться с помощью сигнала user_logged_in от Django.

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

Если, конечно, их не нужно немедленно выходить из системы. Но опять же, они не заметили бы, пока они не попытались все-таки сделать запрос, поэтому приведенное выше решение может просто работать.

Это ответ на запрос Балона:

Да, с примерно 140k сессиями для повторения, я могу понять, почему ответ Гарольда может быть не таким быстрым, как вам может понравиться!

Я бы рекомендовал добавить модель, единственными двумя свойствами которой являются foreign keys для объектов User и Session . Затем добавьте некоторое промежуточное программное обеспечение, которое будет поддерживать эту модель в актуальном состоянии при текущих сеансах пользователя. Раньше я использовал такую ​​настройку; в моем случае я заимствовал модуль sessionprofile из этой системы единого входа для phpBB (см. исходный код в папке «django / sessionprofile»), и это (я думаю) будет соответствовать вашим потребностям.

То, что у вас sessionprofile – это некоторая функция управления где-то в вашем коде, подобном этому (при условии, что те же самые кодовые имена и макет, что и в связанном выше модуле sessionprofile ):

 from sessionprofile.models import SessionProfile from django.contrib.auth.models import User # Find all SessionProfile objects corresponding to a given username sessionProfiles = SessionProfile.objects.filter(user__username__exact='johndoe') # Delete all corresponding sessions [sp.session.delete() for sp in sessionProfiles] 

(Я думаю, что это также удалит объекты SessionProfile , поскольку, как я помню, поведение по умолчанию Django, когда объект, на который ссылается ForeignKey , удаляется, должен каскадировать его, а также удалять объект, содержащий ForeignKey , но если нет, то это достаточно тривиально для удаления содержимого sessionProfiles когда вы закончите.)

Как Тони Абу-Ассале, мне также нужно было вывести пользователей, которые были настроены на неактивность, поэтому я начал с реализации его решения. Через некоторое время я узнал, что промежуточное ПО заставляет запрос БД выполнять все запросы (чтобы проверить, был ли пользователь заблокирован) и, таким образом, вредит производительности на страницах, которые не требуют входа.

У меня есть пользовательский объект пользователя и Django> = 1.7, поэтому то, что я закончил, переопределяет его функцию get_session_auth_hash чтобы аннулировать сеанс, когда пользователь неактивен. Возможная реализация:

 def get_session_auth_hash(self): if not self.is_active: return "inactive" return super(MyCustomUser, self).get_session_auth_hash() 

Для этого необходимо выполнить django.contrib.auth.middleware.SessionAuthenticationMiddleware в settings.MIDDLEWARE_CLASSES django.contrib.auth.middleware.SessionAuthenticationMiddleware

Даже я столкнулся с этой проблемой. Немногие спамеры из Индии продолжают публиковать сообщения о тех Баба и Мольви для любовных решений.

То, что я сделал на момент публикации, только что вставил этот код:

 if request.user.is_active==False: return HttpResponse('You are banned on the site for spaming.') 

Вы можете использовать django-qsessions ( https://github.com/QueraTeam/django-qsessions ).

Это бэкэнд сеанса, который расширяет бэкэнд cached_db Django. Сеансы имеют внешний ключ для пользователя, а также хранят IP и User Agent.

Используя django-qsessions , вы можете легко и эффективно выйти из системы:

 user = User.objects.get(username='abcd') for session in user.session_set.all(): session.delete() 

из django.contrib.sessions.models import Session

удаление сеанса пользователя

 [s.delete() for s in Session.objects.all() if s.get_decoded().get('_auth_user_hash') == user.get_session_auth_hash()] 
Давайте будем гением компьютера.