在某些情况下,在我的Django应用中,我希望能够强制用户通过用户名注销。不一定是当前登录的用户,而是另一个用户。因此,在我看来,request方法没有任何有关我要注销的用户的会话信息。
我熟悉django.auth和auth。注销方法,但是它将请求作为参数。如果我只有用户名,是否存在“ Django方式”将用户注销?还是我必须推出自己的注销SQL?
在某些情况下,在我的Django应用中,我希望能够强制用户通过用户名注销。不一定是当前登录的用户,而是另一个用户。因此,在我看来,request方法没有任何有关我要注销的用户的会话信息。
我熟悉django.auth和auth。注销方法,但是它将请求作为参数。如果我只有用户名,是否存在“ Django方式”将用户注销?还是我必须推出自己的注销SQL?
user.session_set.all().delete()
。免责声明:我是django-qsessions的作者。
Answers:
我认为Django中尚无批准的方法可以做到这一点。
用户ID存储在会话对象中,但已被编码。不幸的是,这意味着您必须遍历所有会话,进行解码和比较...
两步:
首先删除目标用户的会话对象。如果他们从多台计算机登录,则将有多个会话对象。
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]
然后,如果需要,将其锁定...。
user.is_active = False
user.save()
尽管Harold的答案在此特定情况下有效,但我至少可以看到两个重要问题:
Session
将不会使用该模型。为了解决这些问题,我建议您采用另一种方法解决该问题。想法是将用户登录给定会话的日期和上次请求用户注销的时间存储在某处。
然后,每当有人访问您的网站时,如果登录日期小于注销日期,则可以强制注销用户。正如dan所说,立即注销用户或在他的下一个请求访问您的站点之间没有实际区别。
现在,让我们看看针对django 1.3b1的该解决方案的可能实现。分三步:
幸运的是,Django auth系统公开了一个名为的信号user_logged_in
。您只需要注册该信号,然后将当前日期保存在会话中即可。在您的底部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)
我们只需要向模型添加字段和方法User
。有多种实现方法(用户配置文件,模型继承等),各有优缺点。
为了简单起见,我将在这里使用模型继承,如果您使用此解决方案,请不要忘记编写自定义身份验证后端。
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()
然后,如果您要强制注销user johndoe
,则只需执行以下操作:
from myapp.models import MyUser
MyUser.objects.get(username='johndoe').force_logout()
最好的方法是按照dan的建议使用中间件。该中间件将访问request.user
,所以你需要把它后, 'django.contrib.auth.middleware.AuthenticationMiddleware'
你的MIDDLEWARE_CLASSES
设定。
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)
那应该做。
笔记
JOIN
。使用用户个人资料将添加一个额外的查询。直接进行修改User
是明智的最佳方法,但这仍然是一个多毛的话题。如果在现有站点上部署该解决方案,则现有会话可能会遇到一些麻烦,因为这些会话没有'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
功能:
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()
我的应用程序中需要类似的内容。就我而言,如果将用户设置为非活动状态,我想确保该用户是否已经登录,这样他们将被注销并且无法继续使用该站点。阅读这篇文章后,我得出以下解决方案:
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,并且还可以在用户登录时取消设置该字段。后者可以用Django的user_logged_in信号完成。
if request.user.is_authenticated() and not request.user.is_active
这是对Balon的查询的回应:
是的,大约有140k的会话要遍历,我可以理解为什么Harold的答案可能没有您想的那么快!
我建议的方法是添加一个模型,该模型的唯一两个属性是User
和Session
对象的外键。然后添加一些中间件,以使该模型与当前用户会话保持最新。我以前使用过这种设置;就我而言,我sessionprofile
从此Single Sign-On系统中借用了phpBB的模块(请参阅“ django / sessionprofile”文件夹中的源代码),并且此(我认为)可以满足您的需求。
您最终将在代码中的某处有一些管理功能,如下所示(假定代码名称和布局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
删除由a引用的对象时,Django的默认行为是级联它并删除包含的对象ForeignKey
,但如果不删除,则删除该对象很简单。sessionProfiles
完成时的内容。)
作为Tony Abou-Assaleh,我还需要注销设置为非活动状态的用户,因此我首先实现了他的解决方案。一段时间后,我发现中间件正在对所有请求强制执行数据库查询(以检查用户是否被阻止),从而损害了不需要登录的页面的性能。
我有一个自定义用户对象,而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
您还可以使用django直接功能来执行此操作,它将更新并注销该用户的所有其他会话(当前会话除外)。
from django.contrib.auth import update_session_auth_hash
update_session_auth_hash(self.request, user)
这里的update_session_auth_hash文档。
正如其他人所述,您可以遍历数据库中的所有会话,对所有会话进行解码,然后删除属于该用户的会话。但这很慢,尤其是在您的网站流量很高且会话很多的情况下。
如果您需要更快的解决方案,则可以使用会话后端,该后端可让您查询并获取特定用户的会话。在这些会话后端中,会话具有用户的外键,因此您不需要遍历所有会话对象:
db
,cached_db
会话后端)db
会话后端)使用这些后端,可以在一行代码中删除用户的所有会话:
user.session_set.all().delete()
免责声明:我是的作者django-qsessions
。
从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()]
甚至我也面临这个问题。来自印度的垃圾邮件发送者很少继续发布有关Baba和Molvi的爱情解决方案。
我所做的是在发布时刚刚插入以下代码:
if request.user.is_active==False:
return HttpResponse('You are banned on the site for spaming.')