Django 跨登录持久化会话变量
我想在会话变量中保存有关用户首选项的信息。如果用户在注销时选择了一个首选项,然后在以后登录,我希望在不需要重新选择的情况下保留该首选项 Django会话在cookie中维护会话密钥以跟踪用户会话。据我所知,当用户登录时,此密钥会更改 a) 这是否意味着登录时会删除所有会话变量,或者是否存在任何形式的转换 b) 如果无法在登录时保存首选项,手动设置cookie是否是继续操作的最佳方式?我想象这样一个场景:Django 跨登录持久化会话变量,django,Django,我想在会话变量中保存有关用户首选项的信息。如果用户在注销时选择了一个首选项,然后在以后登录,我希望在不需要重新选择的情况下保留该首选项 Django会话在cookie中维护会话密钥以跟踪用户会话。据我所知,当用户登录时,此密钥会更改 a) 这是否意味着登录时会删除所有会话变量,或者是否存在任何形式的转换 b) 如果无法在登录时保存首选项,手动设置cookie是否是继续操作的最佳方式?我想象这样一个场景: 注销时,在cookie中维护首选项 登录时,将首选项复制到会话变量并写入db(通过信号?)
- 注销时,在cookie中维护首选项
- 登录时,将首选项复制到会话变量并写入db(通过信号?)
- 注销时,使用首选项更新Cookie(通过信号?)
我通过将首选项保存在用户的profile对象以及cookie中(这些首选项在任何方面都不敏感),成功地获得了这个功能。当用户登录时,他们的配置文件设置优先。未登录时,当您登录/注销时选择cookie首选项。如果其他用户登录,Django将刷新所有会话(auth/init.py中的request.session.flush())
您最好将用户设置存储在数据库中,并添加一些中间件来获取数据并将其存储在您的请求中。持续存在的用户数据听起来应该存在于类似模型的环境中。我实际上认为您最初的设计是合理的。如果要跨登录/注销边界保存一些会话变量,可以执行以下操作
from functools import wraps
class persist_session_vars(object):
""" Some views, such as login and logout, will reset all session state.
However, we occasionally want to persist some of those session variables.
"""
session_backup = {}
def __init__(self, vars):
self.vars = vars
def __enter__(self):
for var in self.vars:
self.session_backup[var] = self.request.session.get(var)
def __exit__(self, exc_type, exc_value, traceback):
for var in self.vars:
self.request.session[var] = self.session_backup.get(var)
def __call__(self, test_func, *args, **kwargs):
@wraps(test_func)
def inner(*args, **kwargs):
if not args:
raise Exception('Must decorate a view, ie a function taking request as the first parameter')
self.request = args[0]
with self:
return test_func(*args, **kwargs)
return inner
无论从哪个视图调用auth.login/logout,都会抛出此装饰程序。如果您要将它们委托给内置视图,则可以轻松地包装它们
from django.contrib.auth import views
@persist_session_vars(['HTTP_REFERER'])
def login(request, *args, **kwargs):
return views.login(request, *args, **kwargs)
您可以创建一个表单Mixin,该表单Mixin允许您将表单值持久化到用户的会话(不需要登录)。这对于公共表视图报表上的筛选/排序选项等内容非常有用,因为您希望在刷新期间保持其筛选选项的持久性 视图: 表格:
登录时,Django调用
session.flush()
或session.cycle\u key()
,以确保旧会话中没有任何内容保留。这是一种安全措施,可防止会话固定漏洞。因此,在应用此解决方案时,请注意要保留哪些变量
如果您想保持某种状态,则必须在发布登录后恢复该状态
Chase Seibert的解决方案是一个很好的开端,由于代码中的线程安全问题,它非常不安全。您可以在此处找到安全使用的改进版本:
from functools import wraps
class persist_session_vars(object):
"""
Some views, such as login and logout, will reset all session state.
(via a call to ``request.session.cycle_key()`` or ``session.flush()``).
That is a security measure to mitigate session fixation vulnerabilities.
By applying this decorator, some values are retained.
Be very aware what find of variables you want to persist.
"""
def __init__(self, vars):
self.vars = vars
def __call__(self, view_func):
@wraps(view_func)
def inner(request, *args, **kwargs):
# Backup first
session_backup = {}
for var in self.vars:
try:
session_backup[var] = request.session[var]
except KeyError:
pass
# Call the original view
response = view_func(request, *args, **kwargs)
# Restore variables in the new session
for var, value in session_backup.items():
request.session[var] = value
return response
return inner
现在你可以写:
from django.contrib.auth import views
@persist_session_vars(['some_field'])
def login(request, *args, **kwargs):
return views.login(request, *args, **kwargs)
对于基于类的视图(django allauth):
并在url模式中使用该视图:
import allauth.urls
from django.conf.urls import include, url
from . import views
urlpatterns = [
# Views that overlap the default:
url(r'^login/$', views.LoginView.as_view(), name='account_login'),
# default allauth urls
url(r'', include(allauth.urls)),
]
一旦用户登录,它将被写入数据库,但是如果用户没有登录,我仍然希望他们能够设置首选项,一旦他们最终登录,这些首选项将写入userprofile模型,因此问题在于在登录时维护数据,但您无法知道是同一个人还是同一浏览器的另一个用户。因此,您很高兴假设这是同一个人?对于简单的显示设置,是的,但设置数据库对象可以标记为
已授权
,这样只有某些设置写入cookie,而所有设置都写入登录会话。这是一个很好的开始,但不是线程安全的!由于persist\u session\u vars
只实例化一次,因此当两个用户同时登录时,所有用户都在向相同的session\u backup
dict写入数据(即使将其设为对象变量而不是类属性,在这里也不会改变这一点),他们将收到彼此的会话变量。在您的示例中,您的统计信息将处于关闭状态。然而,以这种方式保存个人信息,会在网站上造成巨大的安全/信息泄漏。
from django.contrib.auth import views
@persist_session_vars(['some_field'])
def login(request, *args, **kwargs):
return views.login(request, *args, **kwargs)
import allauth.account.views as auth_views
from django.utils.decorators import method_decorator
@method_decorator(persist_session_vars(['some_field']), name='dispatch')
class LoginView(auth_views.LoginView):
pass
import allauth.urls
from django.conf.urls import include, url
from . import views
urlpatterns = [
# Views that overlap the default:
url(r'^login/$', views.LoginView.as_view(), name='account_login'),
# default allauth urls
url(r'', include(allauth.urls)),
]