我正在编写一个应用程序,我将从 django 和独立应用程序访问数据库。两者都需要进行会话验证,并且两者的会话应该相同。Django 有一个内置的身份验证/会话验证,这就是我正在使用的,现在我需要弄清楚如何为我的独立应用程序重用相同的会话。
我的问题是如何查找特定用户的 session_key?
从外观上看,没有任何东西可以将 auth_user 和 django_session 联系在一起
我正在编写一个应用程序,我将从 django 和独立应用程序访问数据库。两者都需要进行会话验证,并且两者的会话应该相同。Django 有一个内置的身份验证/会话验证,这就是我正在使用的,现在我需要弄清楚如何为我的独立应用程序重用相同的会话。
我的问题是如何查找特定用户的 session_key?
从外观上看,没有任何东西可以将 auth_user 和 django_session 联系在一起
这个答案是在最初的问题发布五年后发布的,但是在搜索这个问题的解决方案时,这个 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()
这就是它的具体细节,如果你想了解更多细节,我有一篇博客文章涵盖了它。
我找到了这个代码片段
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/
尚未验证它,但看起来很简单。
这有点棘手,因为并非每个会话都必须与经过身份验证的用户相关联;Django 的会话框架也支持匿名会话,任何访问您网站的人都会有一个会话,无论他们是否登录。
由于会话对象本身是序列化的,这一点变得更加棘手——因为 Django 无法知道您究竟想要存储哪些数据,它只是将会话数据的字典序列化为字符串(使用 Python 的标准“pickle”模块)并将其填充到您的数据库中。
如果您有会话密钥(将由用户的浏览器作为 cookie 值“sessionid”发送),获取数据的最简单方法是使用该密钥查询会话表以获取会话,这将返回一个 Session目的。然后,您可以调用该对象的“get_decoded()”方法来获取会话数据字典。如果您不使用 Django,可以查看源代码 (django/contrib/sessions/models.py) 以了解会话数据是如何反序列化的。
但是,如果您有用户 id,则需要遍历所有 Session 对象,反序列化每个对象并查找具有名为“_auth_user_id”的键的对象,并且该键的值是用户 id .
修改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 日),了解解决此问题的另一种方法。
彼得罗威尔,感谢您的回复。这是一个巨大的帮助。这就是我为使其正常工作所做的。只需要更改 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()
这对我有用。
当我想踢出垃圾邮件发送者时,我遇到了这个问题。似乎将他们的帐户设置为“非活动”是不够的,因为他们仍然可以参加之前的会话。那么 - 如何删除特定用户的会话,或者如何故意使特定用户的会话过期?
答案是使用该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()