Как ограничить количество одновременных пользователей, входящих в одну учетную запись в Django

мой сайт-это сайт digital marketplace, написанный на языке Django.

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

есть история, что определенный пользователь(который купил контент) отдает имя пользователя/пароль бесплатно многим людям (например, 1,000+ люди в группах Facebook). Эти пользователи 1,000 могут войти в систему, используя это единственное имя пользователя / пароль и просмотреть "заблокированный" цифровой контент не заплатив ни цента.

можно ли ограничить количество одновременных входов в одну учетную запись?

Я нашел этот пакет:

https://github.com/pcraston/django-preventconcurrentlogins

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

3 ответов


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

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

вы хотите разрешить один сессии. Самый простой подход - аннулировать старый сеанс при новом входе в систему бывает:

  • обнаружение / расширение события входа в систему (используйте "user_logged_in" сигнал)
  • для каждого входа в систему удалите другие существующие сеансы у того же пользователя (см. "очистка хранилища сеансов")

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


1 в приложении "пользователи/профили" добавьте командный файл управления

чтобы добавить команду управления, следуйте этому руководству: https://docs.djangoproject.com/en/1.10/howto/custom-management-commands/

2 код команды управления: убивает все сеансы от пользователей, которые имеют более 10 сеансов, вы можете изменить это на 1K, если необходимо, или отправить это значение в качестве параметра команде управления

from django.core.management.base import BaseCommand, CommandError
from django.contrib.sessions.models import Session
from django.contrib.auth.models import User

class Command(BaseCommand):
    def handle(self, *args, **options):
        session_user_dict = {}


        # users with more than 10 sessions - del all

        for ses in Session.objects.all():
            data = ses.get_decoded()
            user_owner = User.objects.filter(pk = data.get('_auth_user_id', None))

            if int(data.get('_auth_user_id', None)) in session_user_dict:
                session_user_dict[int(data.get('_auth_user_id', None))] += 1
            else:
                session_user_dict[int(data.get('_auth_user_id', None))] = 1

        for k,v in session_user_dict.iteritems():
            if v > 10:
                for ses in Session.objects.all():
                    data = ses.get_decoded()
                    if str(k) == data.get('_auth_user_id', None):
                        ses.delete()

3 Необязательное изменение пароля - после убийства сеансов плохих пользователей-измените пароль плохих пользователей на другой. Для этого измените последний цикл в приведенном выше коде

            for k,v in session_user_dict.iteritems():
            if v > 10:
                for ses in Session.objects.all():
                    data = ses.get_decoded()
                    if str(k) == data.get('_auth_user_id', None):
                        ses.delete()
                        theuser =  User.objects.filter(pk=k)
                        #maybe use uuid to pick a password ...
                        theuser.set_password('new_unknown_password')

4 Добавьте команду управления django в crontab каждую минуту / час или когда использование данного руководства: https://www.cyberciti.biz/faq/how-do-i-add-jobs-to-cron-under-linux-or-unix-oses/

Если вы используете виртуальный env , помните, что команда управления, которая выполняется из cron необходимо сначала войти в виртуальный env, вы можете сделать это с помощью a .SH script, при необходимости обратитесь за помощью


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

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

class UserSessions(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='user_sessions')
    session = models.OneToOneField(Session, related_name='user_sessions',
                                   on_delete=models.CASCADE)

    def __str__(self):
        return '%s - %s' % (self.user, self.session.session_key)

если у вас есть свой собственный вид логин, вы можете обновить эту модель себе:

from django.contrib.auth.views import login as auth_login

def login(request):
    auth_login(request)
    if request.user.is_authenticated():
        session = Session.objects.get(session_key=request.session.session_key)
        user_session = UserSession.objects.create(user=request.user, session=session)
    no_of_logins = request.user.user_sessions.count()
    if no_of_logins > 1:  # whatever your limit is
        request.SESSION['EXTRA_LOGIN'] = True
        # Do your stuff here

другой вариант-использовать сигнал. Джанго предоставляет сигналы: user_logged_in, user_login_failed и user_logged_out, если вы используете вид входа Django, то есть.

# signals.py
from django.contrib.auth.signals import user_logged_in
from django.dispatch import receiver

@receiver(user_logged_in)
def concurrent_logins(sender, **kwargs):
    user = kwargs.get('user')
    request = kwargs.get('request')
    if user is not None and request is not None:
        session = Session.objects.get(session_key=request.session.session_key)
        UserSessions.objects.create(user=user, session=session)
    if user is not None:
        request.session['LOGIN_COUNT'] = user.user_sessions.count()

# your login view
def login(request):
     auth_login(request)
     if request.user.is_authenticated() and request.session['LOGIN_COUNT'] > 1:
         # 'LOGIN_COUNT' populated by signal
         request.session['EXTRA_LOGIN'] = True
         # Do your stuff

если EXTRA_LOGIN is True, вы можете перечислить предыдущие сеансы и попросить пользователя выбрать, какие сеансы для выхода из системы. (Не остановите его от входа в систему, иначе он может быть заблокирован - если у него нет доступа к его предыдущим сеансам сейчас)