我正在编写一个应用程序,我将从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()
这就是它的基本要点,如果你想了解更多细节,我会有一篇博文来介绍它.
这有点棘手,因为并非每个会话都必须与经过身份验证的用户相关联; Django的会话框架也支持匿名会话,访问您网站的任何人都会有一个会话,无论他们是否已登录.
会话对象本身被序列化的事实使得这变得更加棘手 - 因为Django无法知道您想要存储哪些数据,它只是将会话数据字典序列化为字符串(使用Python的标准"pickle")模块)和填充到数据库中的东西.
如果你有会话密钥(将由用户的浏览器作为cookie值"sessionid"发送),获取数据的最简单方法就是在Session表中查询具有该密钥的会话,该密钥返回一个Session宾语.然后,您可以调用该对象的"get_decoded()"方法来获取会话数据的字典.如果您不使用Django,可以查看源代码(django/contrib/sessions/models.py)以查看会话数据的反序列化方式.
但是,如果您具有用户标识,则需要循环遍历所有Session对象,对每个对象进行反序列化并查找具有名为"_auth_user_id"的密钥的对象,并且该密钥的值为用户标识.
我找到了这段代码
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_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()