代码之家  ›  专栏  ›  技术社区  ›  Sergey Golovchenko

如何查找特定用户的django会话?

  •  62
  • Sergey Golovchenko  · 技术社区  · 17 年前

    我正在编写一个应用程序,我将从django和独立应用程序访问数据库。两者都需要进行会话验证,会话对它们来说应该是相同的。Django有一个内置的身份验证/会话验证,这就是我正在使用的,现在我需要弄清楚如何为我的独立应用程序重用相同的会话。

    我的问题是如何查找特定用户的session_key?

    从表面上看,没有任何东西将auth_user和django_session联系在一起

    6 回复  |  直到 17 年前
        1
  •  56
  •   FlipperPA Gavin Ballard    9 年前

    这个答案是在原始问题发布五年后发布的,但这个SO线程是搜索这个问题的解决方案时谷歌最热门的结果之一(Django仍然不支持它)。

    对于只关心登录用户会话的用例,我有一个替代解决方案,它使用了额外的 UserSession 模型将用户映射到他们的会话,如下所示:

    from django.conf import settings
    from django.db import models
    from django.contrib.sessions.models import Session
    
    class UserSession(models.Model):
        user = models.ForeignKey(settings.AUTH_USER_MODEL)
        session = models.ForeignKey(Session)  
    

    然后,您可以简单地保存一个新的 用户会话 用户登录时执行以下操作:

    from django.contrib.auth.signals import user_logged_in
    
    def user_logged_in_handler(sender, request, user, **kwargs):
        UserSession.objects.get_or_create(user = user, session_id = request.session.session_key)
    
    user_logged_in.connect(user_logged_in_handler)
    

    最后,当您想列出(并可能清除)特定用户的会话时:

    from .models import UserSession
    
    def delete_user_sessions(user):
        user_sessions = UserSession.objects.filter(user = user)
        for user_session in user_sessions:
            user_session.session.delete()
    

    这就是它的细节,如果你想了解更多细节,我有一个 blog post 覆盖它。

        2
  •  31
  •   michael    15 年前

    我找到了这段代码片段

    from django.contrib.sessions.models import Session
    from django.contrib.auth.models import User
    
    session_key = '8cae76c505f15432b48c8292a7dd0e54'
    
    session = Session.objects.get(session_key=session_key)
    uid = session.get_decoded().get('_auth_user_id')
    user = User.objects.get(pk=uid)
    
    print user.username, user.get_full_name(), user.email
    

    在这里 http://scottbarnham.com/blog/2008/12/04/get-user-from-session-key-in-django/

    尚未验证,但看起来很直接。

        3
  •  28
  •   James Bennett    17 年前

    这有点棘手,因为并非每个会话都必须与经过身份验证的用户相关联;Django的会话框架也支持匿名会话,任何访问你网站的人都会有一个会话,无论他们是否登录。

    由于会话对象本身是序列化的,这使得情况变得更加棘手——因为Django无法知道你到底想存储哪些数据,它只是将会话数据字典序列化为字符串(使用Python的标准“pickle”模块),并将其填充到你的数据库中。

    如果你有会话密钥(它将由用户的浏览器作为cookie值“sessionid”发送),获取数据的最简单方法就是在session表中查询具有该密钥的会话,这将返回一个session对象。然后,您可以调用该对象的“get_coded()”方法来获取会话数据字典。如果你没有使用Django,你可以查看源代码(Django/contrib/sessions/models.py),看看会话数据是如何反序列化的。

    但是,如果你有用户id,你需要遍历所有Session对象,对每个对象进行反序列化,并寻找一个具有名为“_auth_user_id”的键,并且该键的值是用户id的对象。

        4
  •  26
  •   Peter Rowell    11 年前

    修改 django_session 表以添加显式 user_id 可以让生活变得容易得多。假设你这样做(或类似的事情),这里有四种方法可以根据你的喜好进行修改:

    叉起 django.contrib.session 代码。我知道,我知道,这是一个可怕的建议。但它只有500行,包括所有后端,减去测试。破解起来相当简单。只有当你打算认真重新安排事情时,这才是最好的路线。

    如果你不想分叉,你可以尝试连接到 Session.post_save 在那里发信号和移动。

    或者你可以使用MonkeyPatch contrib.session.models.Session.save() 。只需包装现有方法(或创建一个新方法),分解/合成所需的任何值,将其存储在新字段中,然后 super(Session, self).save() .

    还有一种方法是放入2个(是的,两个)中间件类——一个在之前,一个在之后 SessionMiddleware 在你的 settings.py 文件。这是因为中间件的处理方式。后面列出的那个 会话中间件 将在入站请求上获取已附加会话的请求。前面列出的请求可以对响应进行任何处理和/或更改/重新保存会话。

    我们使用了最后一种技术的变体,为搜索引擎蜘蛛创建了伪会话,使它们能够特殊访问通常仅限成员的材料。我们还检测入站链接,其中 REFERER 字段来自关联的搜索引擎,我们赋予用户对该文章的完全访问权限。

    更新:

    我的答案现在已经相当古老了,尽管它仍然基本正确。请参阅下面@Gavin_Ballard的最新答案(2014年9月29日),了解解决这个问题的另一种方法。

        5
  •  7
  •   Joe    15 年前

    彼得·罗威尔,谢谢你的回复。这是一个巨大的帮助。这就是我让它工作的方法。只需更改djang.contrib.sessions中的一个文件。

    在django/contrib/sessions/models.py中,将user_id添加到表中(手动添加到DB表中或删除表并运行manage.py syncdb)。

    class Session(models.Model):
    
        ...
    
        user_id = models.IntegerField(_('user_id'), null=True)
    
        ...
    
        def save(self, *args, **kwargs):
            user_id = self.get_decoded().get('_auth_user_id')
            if ( user_id != None ):
                self.user_id = user_id
    
            # Call the "real" save() method.
            super(Session, self).save(*args, **kwargs)
    

    现在,在您登录的视图中(如果您使用django的基本登录,则必须覆盖它)

    # On login, destroy all prev sessions
            # This disallows multiple logins from different browsers
            dbSessions = Session.objects.filter( user_id = request.user.id )
            for index, dbSession in enumerate( dbSessions ):
                if ( dbSession.session_key != request.session.session_key ):
                    dbSession.delete()
    

    这对我奏效了。

        6
  •  6
  •   hwjp    11 年前

    当我想赶走一个垃圾邮件发送者时,我遇到了这个问题。似乎将他们的帐户设置为“非活动”是不够的,因为他们仍然可以参加上一次会议。那么,如何删除特定用户的会话,或者如何故意使特定用户的一个会话过期呢?

    答案是用户 last_login 字段来跟踪会话被禁用的时间,这告诉您 expire_date 两周后,您可以在会话表上执行有用的筛选:

    from django.contrib.sessions.models import Session
    from django.contrib.auth.models import User
    from datetime import datetime
    from dateutil.relativedelta import relativedelta
    
    baduser = User.objects.get(username="whoever")     
    two_weeks = relativedelta(weeks=2)
    two_hours = relativedelta(hours=2)
    expiry = baduser.last_login + two_weeks
    sessions = Session.objects.filter(
        expire_date__gt=expiry - two_hours,
        expire_date__lt=expiry + two_hours
    ) 
    print sessions.count() # hopefully a manageable number
    
    for s in sessions:
        if s.get_decoded().get('_auth_user_id') == baduser.id:
            print(s)
            s.delete()