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

时间:2008-10-25 05:27:42

标签: django authentication session

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

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

从它的外观来看,没有任何东西将auth_user和django_session联系在一起

6 个答案:

答案 0 :(得分:44)

这个答案是在原始问题发布五年之后发布的,但是这个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()

这是它的基本要点,如果你想了解更多细节我有blog post覆盖它。

答案 1 :(得分:28)

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

由于会话对象本身是序列化的,因此更加棘手 - 由于Django无法确切知道要存储哪些数据,因此它只是将会话数据字典序列化为字符串(使用Python的标准) “pickle”模块)并将其存入您的数据库。

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

但是,如果您拥有用户ID,则需要循环遍历所有Session对象,对每个对象进行反序列化并查找具有名为“_auth_user_id”的密钥的对象,并且该密钥的值为用户ID。

答案 2 :(得分:26)

我找到了这段代码

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 :(得分:24)

修改django_session表以添加显式user_id可以让生活变得更轻松。假设你这样做(或类似的东西),这里有四种方法可以根据自己的喜好进行调整:

分叉django.contrib.session代码。我知道,我知道,这是一个可怕的建议。但它只有500行,包括所有后端和减去测试。破解非常简单。只有当你要做一些严肃的重新安排时,这才是最好的路线。

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

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

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

我们在最后一项技术上使用了一种变体来为搜索引擎蜘蛛创建伪会话,以便为他们提供对通常仅限成员的材料的特殊访问。我们还检测{{1}}字段来自关联搜索引擎的入站链接,我们为用户提供对该文章的完全访问权限。

<强>更新

我的答案现在相当古老,尽管它仍然是正确的。有关此问题的另一种解决方法,请参阅@ Gavin_Ballard最近的答案(2014年9月29日)。

答案 4 :(得分: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()

这对我有用。

答案 5 :(得分: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()