我正在编写一个无法同时登录的用户系统。 如果帐户处于登录状态某处,并且有人在其他位置登录同一帐户。后者将登录。之前的将被注销。 我使用的模型与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仍将处于登录状态,并且不会注销。我怎么能防止这个问题?
答案 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
对象在User
和Session
之间建立了链接。现在我们可以实现一个登录挂钩:一个用户登录时触发的信号。在这种情况下,我们执行两件事:
Session
的所有UserSession
s(和相应的User
s);和Session
和相应的UserSession
,我们可以稍后删除。像: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