Python 在Django应用程序中每个用户只允许一个活动会话
我想限制登录用户只有一个活动会话,即,如果用户使用新会话ID登录,则应终止旧会话。 我已经在网上找到了很多帮助: 及 我实现了中间件解决方案,并进行了一些额外的检查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
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
可以工作。