如何强制用户在django中注销?

时间:2009-06-05 01:27:24

标签: django django-authentication

在我的django应用程序中,在某些条件下,我需要能够强制用户通过用户名注销。不一定是当前登录的用户,而是其他一些用户。因此,我视图中的请求方法没有关于我要注销的用户的任何会话信息。

我熟悉django.auth,并使用auth.logout方法,但它需要 request 作为参数。如果我拥有的是用户名,是否有“django-way”来记录用户?或者我是否必须滚动自己的注销sql?

10 个答案:

答案 0 :(得分:71)

我认为还没有一种制裁方法可以在Django中做到这一点。

用户标识存储在会话对象中,但它是经过编码的。不幸的是,这意味着你必须遍历所有会话,解码和比较......

两个步骤:

首先删除目标用户的会话对象。如果他们从多台计算机登录,则会有多个会话对象。

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()

答案 1 :(得分:53)

虽然哈罗德的答案适用于这个具体案例,但我至少可以看到两个重要问题:

  1. 此解决方案只能与数据库session engine一起使用。在其他情况下(缓存,文件,cookie),将不使用Session模型。
  2. 当数据库中的会话数和用户数增加时,效率会非常低。
  3. 要解决这些问题,我建议您采取另一种方法解决问题。我们的想法是存储用户登录给定会话的日期,以及您上次请求用户注销的日期。

    然后,当有人访问您的网站时,如果登录日期低于注销日期,您可以强制注销该用户。正如dan所说,立即注销用户或下次请求访问您的网站没有实际区别。

    现在,让我们看看 django 1.3b1 可能实现此解决方案。分三步:

    1。在会话中存储上次登录日期

    幸运的是,Django auth系统公开了一个名为user_logged_in的{​​{3}}。您只需注册该信号,并将当前日期保存在会话中。在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模型中添加字段和方法。有多种方法可以实现(signaluser profiles等)各有利弊。

    为了简单起见,我将在这里使用模型继承,如果你选择这个解决方案,不要忘记model inheritance

    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。实施访问检查

    这里最好的方法是使用write a custom authentication backend作为dan建议。此中间件将访问request.user,因此您需要在'django.contrib.auth.middleware.AuthenticationMiddleware'设置中 <{strong> 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是性能最佳的最佳方式,但它仍然是middleware
    • 如果您在现有网站上部署该解决方案,您可能会遇到现有会话的问题,而现有会话不会拥有'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()
      

答案 2 :(得分:44)

我在我的应用中需要类似的东西。在我的情况下,如果用户设置为非活动状态,我想确保用户是否已登录,他们将被注销并且无法继续使用该网站。阅读本文后,我找到了以下解决方案:

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信号完成。

答案 3 :(得分:7)

也许,一些中间件引用了被强制注销的用户列表。下次用户尝试执行任何操作时,请将其注销,然后重定向等等。

除非他们当然需要立即退出。但话说回来,他们不会注意到他们下次尝试提出请求,所以上述解决方案可能会起作用。

答案 4 :(得分:5)

这是对Balon的疑问的回应:

是的,有大约140,000个会话要迭代我可以看出为什么哈罗德的回答可能没有你想要的那么快!

我建议的方法是添加一个模型,其中只有两个属性是UserSession对象的外键。然后添加一些中间件,使该模型与当前用户会话保持同步。我之前使用过这种设置;就我而言,我借用了Single Sign-On system for phpBB中的sessionprofile模块(参见“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引用的对象时,Django的默认行为是级联它并删除包含的对象ForeignKey,但如果没有,那么在完成后删除sessionProfiles的内容就足够了。)

答案 5 :(得分:2)

作为Tony Abou-Assaleh,我还需要注销那些被设置为非活动状态的用户,所以我开始实施他的解决方案。一段时间后,我发现中间件正在强制对所有请求进行数据库查询(以检查用户是否被阻止),从而在不需要登录的页面上损害性能。

我有一个自定义用户对象和Django&gt; = 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

答案 6 :(得分:0)

正如其他人所述,您可以在数据库中迭代所有会话,解码所有会话,并删除属于该用户的会话。但它很慢,特别是如果您的网站流量很大且会话很多。

如果您需要更快的解决方案,则可以使用会话后端,以便查询和获取特定用户的会话。在这些会话后端中,Session有一个用户的外键,因此您不需要迭代所有会话对象:

使用这些后端,删除用户的所有会话可以在一行代码中完成:

user.session_set.all().delete()

免责声明:我是django-qsessions的作者。

答案 7 :(得分:0)

您还可以使用直接django功能来执行此操作,它将为用户更新注销所有其他会话(当前会话除外)。

from django.contrib.auth import update_session_auth_hash
update_session_auth_hash(self.request, user)

答案 8 :(得分:-1)

来自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()]

答案 9 :(得分:-1)

即使我遇到了这个问题。很少有来自印度的垃圾邮件发送者继续发布关于Baba和Molvi的爱情解决方案。

我所做的是在发布时刚插入此代码:

{{1}}