Python 在Django应用程序中每个用户只允许一个活动会话

Python 在Django应用程序中每个用户只允许一个活动会话,python,django,apache2,mod-wsgi,django-authentication,Python,Django,Apache2,Mod Wsgi,Django Authentication,我想限制登录用户只有一个活动会话,即,如果用户使用新会话ID登录,则应终止旧会话。 我已经在网上找到了很多帮助: 及 我实现了中间件解决方案,并进行了一些额外的检查 class OnlyOneUserMiddleware(object): """ Middleware to ensure that a logged-in user only has one session active. Will kick out any previous session. """ def process_r

我想限制登录用户只有一个活动会话,即,如果用户使用新会话ID登录,则应终止旧会话。 我已经在网上找到了很多帮助: 及

我实现了中间件解决方案,并进行了一些额外的检查

class OnlyOneUserMiddleware(object):
"""
Middleware to ensure that a logged-in user only has one session active.
Will kick out any previous session. 
"""
def process_request(self, request):
    if request.user.is_authenticated():
        try:
            cur_session_key = request.user.get_profile().session_key
            if cur_session_key and cur_session_key != request.session.session_key:
                # Default handling... kick the old session...
                Session.objects.get(session_key=cur_session_key).delete()
            if not cur_session_key or cur_session_key != request.session.session_key:
                p = request.user.get_profile()
                p.session_key = request.session.session_key
                p.save()
        except ObjectDoesNotExist:
            pass
到目前为止,一切都很好。。。在Django dev服务器(manage.py runserver)上,一切都正常工作,它启动了旧会话

…但是当使用Apache(与mod_wsgi一起使用)时,它不起作用

我试图找到任何有关这方面的信息,但到目前为止运气不好

我发现的最接近的问题是,但这是一种“相反”的问题

任何帮助都将不胜感激

编辑:我在删除会话之前添加了调试打印。。。 以下是Apache的error.log中的一个片段:

[Fri Jan 20 09:56:50 2012] [error] old key = f42885ccb7f33b6afcb2c18fca14f44a
[Fri Jan 20 09:56:50 2012] [error] new key = ce4cfb672e6025edb8ffcd0cf2b4b8d1
[Fri Jan 20 09:57:14 2012] [error] old key = f42885ccb7f33b6afcb2c18fca14f44a
[Fri Jan 20 09:57:14 2012] [error] new key = 0815c56241ac21cf4b14b326f0aa7e24
前两个谎言来自我参加第一次会议(Firefox)的时候

最后两个是我参加第二次会议(Chromium)时的记录

。。。原来旧的会话记录没有被删除

我正在运行与devserver完全相同的PostgreSQL实例

Edit2:原来我的代码有问题。。。在会话中找不到新会话密钥时失败

这是固定代码。。。try..except现在位于正确的位置

class OnlyOneUserMiddleware(object):
    """
    Middleware to ensure that a logged-in user only has one session active.
    Will kick out any previous session. 
    """
    def process_request(self, request):
        if request.user.is_authenticated():
            cur_session_key = request.user.get_profile().session_key
            if cur_session_key and cur_session_key != request.session.session_key:
                # Default handling... kick the old session...
                try:
                    s = Session.objects.get(session_key=cur_session_key)
                    s.delete()
                except ObjectDoesNotExist:
                    pass
            if not cur_session_key or cur_session_key != request.session.session_key:
                p = request.user.get_profile()
                p.session_key = request.session.session_key
                p.save()

您可以始终使用此方法,尽管不推荐,但它是有效的

my_old_sessions = Session.objects.all()
for row in my_old_sessions:
   if row.get_decoded().get("_username") == request.user.username:
      row.delete()
在验证用户之前,您应该在login()函数中实现上面的代码

当然,只有当您有一个login()函数方法在其会话中存储用户用户名时,此方法才有效,如下所示:

request.session["_username"] = request.user.username

如果您使用这种方法,请记住在进行这些更改后运行服务器之前清空数据库中的所有会话,因为这会导致KeyLookUp错误。

确实有很多类似的问题,但我的解决方案如下

当用户登录时,检查所有活动会话并删除具有相同
user.id
的会话。对于较小的网站,这应该很好

# __init__.py
# Logs user out from all other sessions on login, django 1.8

from django.contrib.sessions.models import Session
from django.contrib.auth.signals import user_logged_in
from django.db.models import Q
from django.utils import timezone

def limit_sessions(sender, user, request, **kwargs):
    # this will be slow for sites with LOTS of active users

    for session in Session.objects.filter(
        ~Q(session_key = request.session.session_key),
        expire_date__gte = timezone.now()
    ):
        data = session.get_decoded()
        if data.get('_auth_user_id', None) == str(user.id):
            # found duplicate session, expire it
            session.expire_date = timezone.now()
            session.save()

    return

user_logged_in.connect(limit_sessions)

我觉得,不知何故,django.contrib.auth信号可以在这里有所帮助。登录时,使旧用户会话无效

当你说“不起作用”时,到底是什么不起作用?在数据库中仍然可以看到旧会话吗?如果您在
会话
delete之前发出打印/日志调用,您是否看到在
mod_wsgi
下执行的操作?@AdamKG:感谢您为我指明了正确的方向!如果会话引擎是cache\u db,我认为我们还需要手动从缓存中删除sessionkey,对吗?
user.get\u profile()
do做什么?您比较了这两种方法的性能吗?我的意思是,通过信号和中间件。如果使用中间件,所有请求都应该通过handle\u请求。如果使用信号,虽然只有登录事件命中函数,但它会在会话表上迭代。。。所以,想想性能,哪一个更好…我确实说过“对于较小的网站”:)-你可能想像OP一样存储密钥,但只在登录时检查它,就像我为更高性能的解决方案所做的那样。是的,我看到你提到过对于较小的网站:-)我只是想知道什么对较大的网站更好:-)如果你已经在使用用户数据库,这意味着所有用户都必须先注销才能正常工作。_用户名不再位于会话对象的get_decoded()中(Django 1.8)。但是,用户id是,所以
row.get\u decoded().get(“\u auth\u user\u id”)==request.user.pk
可以工作。