如何防止在Django中多次登录

时间:2018-06-13 09:40:09

标签: django session

我正在编写一个无法同时登录的用户系统。 如果帐户处于登录状态某处,并且有人在其他位置登录同一帐户。后者将登录。之前的将被注销。 我使用的模型与oneToOneField关联到User模型,并保存此用户的会话ID。 代码如下所示。

from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
from .myModels import JSONField

class Profile(models.Model):
    user = models.OneToOneField(User, models.CASCADE)
    sessionids = JSONField(null=True)


@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
    if created:
        Profile.objects.create(user=instance)

JSONField是一个使用textField存储JSON字符串的字段。 当用户登录时,我将获取该用户的所有会话ID并删除所有会话ID。然后我将当前会话ID添加到配置文件。通过这样做,我可以退出以前的位置。代码如下所示。

def login(request):
    if request.method == "POST":
        if request.user.is_authenticated:
            return HttpResponse("the user session is authenticated.")

        username = request.POST.get('username', '')
        password = request.POST.get('password', '')

        user = auth.authenticate(username=username, password=password)

        if user is not None and user.is_active:
            auth.login(request, user)

            #remove cur user all sessions
            sessionidsToDelete = request.user.profile.sessionids
            if sessionidsToDelete != None:
                sessions = Session.objects.filter(session_key__in=sessionidsToDelete)
                for session in sessions:
                    session.delete()

            #add cur user sessions
            sessionidsToStore = user.profile.sessionids
            print("sessionidsToStore = ")
            print(sessionidsToStore)
            print("sessionidsToDelete = ")
            print(sessionidsToDelete)

            if sessionidsToStore== None:
                sessionidsToStore = []
            else:
                sessionidsToStore = list(set(sessionidsToStore) - set(sessionidsToDelete))
            print("sessionidsToStore = ")
            print(sessionidsToStore)
            sessionidsToStore.append(request.session.session_key)
            user.profile.sessionids = json.dumps(sessionidsToStore)
            user.profile.save()

            rotate_token(request)
            return HttpResponse("login sucessful")
        elif user.is_active == False:
            userNotActivedHttpresponse = HttpResponse()
            userNotActivedHttpresponse.status_code = 605
            userNotActivedHttpresponse.reason_phrase = "This user not active"
            return userNotActivedHttpresponse
        else:
            return HttpResponse("Please Input the correct username and password")
    else:
        return HttpResponseBadRequest("Please use POST to login")

但我认为会发生一些事情。当有两个人想要同时登录同一个帐户时。 例如,有两个人知道相同的帐户。 他们同时登录。在A移除所有其他会话ID之后,B可能会将B的会话ID附加到配置文件。在这种情况下,A和B仍将处于登录状态,并且不会注销。我怎么能防止这个问题?

2 个答案:

答案 0 :(得分:2)

我认为你把事情变得非常复杂,通过在UserProfile等存储数据然后有信号,你引入了很多层次,并且在每个层面都会出现问题。

我们在这里基本上需要两件事:一张将User映射到相应设置的表格。我们可以使用UserSession模型实现此功能:

# models.py

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, on_delete=models.CASCADE)
    session = models.OneToOneField(Session, on_delete=models.CASCADE)

因此UserSession对象在UserSession之间建立了链接。现在我们可以实现一个登录挂钩:一个用户登录时触发的信号。在这种情况下,我们执行两件事:

  1. 我们会删除有效的Session的所有UserSession s(和相应的User s);和
  2. 我们会创建一个新的Session和相应的UserSession,我们可以稍后删除。像:
  3. from django.contrib.auth import user_logged_in
    from django.dispatch.dispatcher import receiver
    
    @receiver(user_logged_in)
    def remove_other_sessions(sender, user, request, **kwargs):
        # remove other sessions
        Session.objects.filter(usersession__user=user).delete()
    
        # save current session
        request.session.save()
    
        # create a link from the user to the current session (for later removal)
        UserSession.objects.get_or_create(
            user=user,
            session=Session.objects.get(pk=request.session.session_key)
        )

答案 1 :(得分:-1)

由于您希望用户一次只有一个会话,因此可以在注销之前先注销。

...
if user is not None and user.is_active:
    auth.logout(request)
    auth.login(request, user)

注销文档:https://docs.djangoproject.com/en/3.0/topics/auth/default/#django.contrib.auth.logout