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

时间:2012-01-19 14:01:14

标签: python django apache2 mod-wsgi django-authentication

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

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

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开发服务器(manage.py runserver)上一切正常,它踢了旧会话......

...但是当使用Apache(使用mod_wsgi)时,它不起作用!

我试图找到关于此的任何信息,但到目前为止没有运气......

我找到的最接近的是this,但这是一个“相反”的问题......

非常感谢任何帮助。

编辑:我在删除会话之前添加了调试打印... 这是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)

...事实证明旧的Session记录没有被删除...... ???

我正在运行与完全相同的PostgreSQL实例,就像我使用devserver一样......

Edit2:事实证明我的代码有问题...当在Session中找不到新的Session_key时失败...

这是固定代码...... 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()

3 个答案:

答案 0 :(得分:1)

你总是可以使用这种方法虽然不推荐,但它确实有效。

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()函数方法才能在他的会话中存储USERS用户名,如下所示:

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

如果您使用此方法,请记住在进行这些更改之后清空所有会话的数据库,然后再运行服务器,因为它会引发KeyLookUp错误。

答案 1 :(得分:1)

到处都有很多类似的问题,但这是我的解决方案。

当用户登录时,查看所有活动会话并删除具有相同# __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) 的会话。对于较小的网站,这应该没问题。

{{1}}

答案 2 :(得分:0)

I feel that, somehow, django.contrib.auth signals could help here. On login, invalidate older user sessions.