如何强制用户注销django?
在我的Django应用程序在某些条件下,我需要能够强制用户通过用户名注销。 不一定是当前login的用户,而是其他用户。 所以我的视图中的请求方法没有关于我想要注销的用户的任何会话信息。
我熟悉django.auth和auth.logout方法,但它将请求作为参数。 有没有一个“Django的方式”login用户,如果我只是用户名? 或者我必须推出我自己的注销sql?
我认为在Django中还没有一个被认可的方法。
用户标识存储在会话对象中,但被编码。 不幸的是,这意味着你将不得不迭代所有会话,解码和比较…
两个步骤:
首先删除目标用户的会话对象。 如果他们从多台计算机login,他们将有多个会话对象。
from django.contrib.sessions.models import Session from django.contrib.auth.models import User # grab the user in question user = User.objects.get(username='johndoe') [s.delete() for s in Session.objects.all() if s.get_decoded().get('_auth_user_id') == user.id]
那么,如果你需要,locking他们….
user.is_active = False user.save()
尽pipe哈罗德的答案在这个具体案例中起作用,但我至less可以看到两个重要的问题:
- 此解决scheme只能与数据库会话引擎一起使用 。 在其他情况下(caching,文件,cookie),
Session
模型将不会被使用。 - 当数据库中的会话和用户数量增长时,这变得相当低效。
为了解决这些问题,我build议你在这个问题上采取另一种方法。 这个想法是存储用户login某个会话的date,以及上一次请求用户注销的地方。
然后,只要有人访问您的网站,如果logindate低于注销date,您可以强制注销用户。 正如丹说的那样,立即注销用户或者在他的下一个请求到你的网站之间没有实际的区别。
现在,让我们看看这个解决scheme的可能实现,为Django 1.3b1 。 分三步:
1.在会话中存储上次logindate
幸运的是,Django身份validation系统公开了一个名为user_logged_in
的信号 。 您只需注册该信号,并将当前date保存在会话中。 在models.py
的底部:
from django.contrib.auth.signals import user_logged_in from datetime import datetime def update_session_last_login(sender, user=user, request=request, **kwargs): if request: request.session['LAST_LOGIN_DATE'] = datetime.now() user_logged_in.connect(update_session_last_login)
2.为用户请求强制注销
我们只需要在User
模型中添加一个字段和一个方法。 有多种方法可以实现( 用户configuration文件 , 模型inheritance等),每个方法都有优缺点。
为了简单起见,我将在这里使用模型inheritance,如果您使用此解决scheme,请不要忘记编写自定义身份validation后端 。
from django.contrib.auth.models import User from django.db import models from datetime import datetime class MyUser(User): force_logout_date = models.DateTimeField(null=True, blank=True) def force_logout(self): self.force_logout_date = datetime.now() self.save()
那么,如果你想强制注销用户johndoe
,你只需要:
from myapp.models import MyUser MyUser.objects.get(username='johndoe').force_logout()
3.执行访问检查
这里最好的方法是使用中间件作为丹build议。 这个中间件将访问request.user
,所以你需要把它放在MIDDLEWARE_CLASSES
设置中的'django.contrib.auth.middleware.AuthenticationMiddleware'
之后 。
from django.contrib.auth import logout class ForceLogoutMiddleware(object): def process_request(self, request): if request.user.is_authenticated() and request.user.force_logout_date and \ request.session['LAST_LOGIN_DATE'] < request.user.force_logout_date: logout(request)
这应该做到这一点。
笔记
- 请注意为用户存储额外字段的性能影响。 使用模型inheritance将会添加一个额外的
JOIN
。 使用用户configuration文件将添加额外的查询。 直接修改User
是性能最好的方式,但它仍然是一个多毛的话题 。 -
如果您在现有网站上部署该解决scheme,那么现有会话可能会遇到一些问题,而这些会话将不具有
'LAST_LOGIN_DATE'
键。 你可以适应一下中间件代码来处理这种情况:from django.contrib.auth import logout class ForceLogoutMiddleware(object): def process_request(self, request): if request.user.is_authenticated() and request.user.force_logout_date and \ ( 'LAST_LOGIN_DATE' not in request.session or \ request.session['LAST_LOGIN_DATE'] < request.user.force_logout_date ): logout(request)
-
在Django 1.2.x中,没有
user_logged_in
信号。 回退到重写login
function:from django.contrib.auth import login as dj_login from datetime import datetime def login(request, user): dj_login(request, user) request.session['LAST_LOGIN_DATE'] = datetime.now()
我需要在我的应用程序类似的东西。 在我的情况下,如果用户被设置为非活动状态,我想确定用户是否已经login,他们将被注销,无法继续使用该站点。 阅读这篇文章后,我来到了以下解决scheme:
from django.contrib.auth import logout class ActiveUserMiddleware(object): def process_request(self, request): if not request.user.is_authenticated(): return if not request.user.is_active: logout(request)
只需在你的设置中添加这个中间件,然后离开你。 在更改密码的情况下,您可以在userprofile模型中引入一个强制用户注销的新字段,检查字段的值而不是上面的is_active,还可以在用户login时取消设置字段。后者可以用Django的user_logged_in信号完成。
也许,一些中间件引用了一个已经被迫注销的用户列表。 下一次用户尝试做任何事情,然后注销,redirect他们等。
除非当然,他们需要立即注销。 但是,再次,他们不会注意,直到他们下一个尝试提出请求,所以上述解决scheme可能会正常工作。
这是对Balon的查询的回应:
是的,有大约140k的会议迭代我可以看到为什么哈罗德的答案可能不会像你可能喜欢的那样快!
我build议的方式是添加一个模型,只有两个属性是User
和Session
对象的外键。 然后添加一些中间件,使当前用户会话的模型保持最新。 我之前使用过这种设置, 在我的情况下,我从phpBB的这个单点login系统 (参见“django / sessionprofile”文件夹中的源代码)借用sessionprofile
模块,这个(我认为)将适合您的需要。
你最终会得到的是代码中某处的某个pipe理函数(假设与上面链接的sessionprofile
模块中的代码名称和布局相同):
from sessionprofile.models import SessionProfile from django.contrib.auth.models import User # Find all SessionProfile objects corresponding to a given username sessionProfiles = SessionProfile.objects.filter(user__username__exact='johndoe') # Delete all corresponding sessions [sp.session.delete() for sp in sessionProfiles]
(我认为这也将删除SessionProfile
对象,从我记得,当一个ForeignKey
引用的对象被删除时,Django的默认行为是级联它,也删除包含ForeignKey
的对象,但如果不是,那么它是微不足道的完成后删除sessionProfiles
的内容。)
作为Tony Abou-Assaleh,我还需要注销那些被设置为不活动的用户,所以我开始实施他的解决scheme。 经过一段时间后,我发现中间件强制对所有请求进行数据库查询(以检查用户是否被阻止),从而损害了不需要login的页面的性能。
我有一个自定义的用户对象和Django> = 1.7,所以我最终做的是重写它的get_session_auth_hash
函数使用户无效时会话失效。 一个可能的实现是:
def get_session_auth_hash(self): if not self.is_active: return "inactive" return super(MyCustomUser, self).get_session_auth_hash()
为了这个工作, django.contrib.auth.middleware.SessionAuthenticationMiddleware
应该在settings.MIDDLEWARE_CLASSES
即使我面对这个问题。 很less有来自印度的垃圾邮件发送者一直在发布有关爱情解决scheme的Baba和Molvi。
我所做的只是在发布时插入了这段代码:
if request.user.is_active==False: return HttpResponse('You are banned on the site for spaming.')
从django.contrib.sessions.models导入会话
删除用户会话
[s.delete() for s in Session.objects.all() if s.get_decoded().get('_auth_user_hash') == user.get_session_auth_hash()]