60

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

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

从外观上看,没有任何东西可以将 auth_user 和 django_session 联系在一起

4

6 回答 6

56

这个答案是在最初的问题发布五年后发布的,但是在搜索这个问题的解决方案时,这个 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)  

UserSession然后,您可以在用户登录时简单地保存一个新实例:

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

这就是它的具体细节,如果你想了解更多细节,我有一篇博客文章涵盖了它。

于 2014-09-29T10:19:05.737 回答
31

我找到了这个代码片段

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/

尚未验证它,但看起来很简单。

于 2011-06-04T17:36:31.230 回答
28

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

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

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

但是,如果您有用户 id,则需要遍历所有 Session 对象,反序列化每个对象并查找具有名为“_auth_user_id”的键的对象,并且该键的值是用户 id .

于 2008-10-25T06:44:49.583 回答
26

修改django_session表以添加显式user_id可以使生活更轻松。假设您这样做(或类似的事情),这里有四种方法可以根据您的喜好调整事物:

分叉django.contrib.session代码。我知道,我知道,这是一个可怕的建议。但它只有 500 行,包括所有后端并减去测试。破解非常简单。仅当您要对事情进行认真的重新安排时,这才是最佳途径。

如果您不想分叉,您可以尝试连接到Session.post_save信号并在那里进行调整。

或者你可以 MonkeyPatch contrib.session.models.Session.save()。只需包装现有方法(或创建一个新方法),突破/合成您需要的任何值,将它们存储在新字段中,然后super(Session, self).save().

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

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

更新:

我的答案现在很古老,尽管它仍然大部分是正确的。请参阅下面@Gavin_Ballard 的最新答案(2014 年 9 月 29 日),了解解决此问题的另一种方法。

于 2008-10-25T23:33:16.977 回答
7

彼得罗威尔,感谢您的回复。这是一个巨大的帮助。这就是我为使其正常工作所做的。只需要更改 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()

这对我有用。

于 2011-02-03T22:16:26.497 回答
6

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

答案是使用该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()
于 2012-07-18T11:26:19.447 回答