2011-11-24 168 views
2

我想在会话变量中保存关于用户首选项的信息。如果用户在注销时选择首选项,然后再登录,我希望维护首选项而无需重新选择它。在登录时保持会话变量

Django会话在Cookie中维护会话密钥以跟踪用户会话。我的理解是,这关键是改变时,在用户登录。

一)这是否意味着所有的会话变量是在登录删除或有任何形式的逾越节

b)在没有的情况下能够通过登录保存偏好设置,是否需要手动设置Cookie才能继续?我想像一个场景,如:

  • 而注销,维持在饼干上登录
  • 的喜好,喜好复制到会话变量,并写入到数据库
  • 上注销,与喜好更新饼干(通过信号?) (通过信号?)

更新

我设法在用户节省喜好来获取此功能配置文件对象以及Cookie中(这些首选项不以任何方式敏感)。用户登录后,他们的个人资料设置将优先。如果没有登录,则cookie偏好选择

回答

2

当你登入/登出Django会刷新所有会话如果(request.session.flush()在认证/ 初始化的.py)其他用户登录。

您最好将用户设置存储在数据库中,并添加一些中间件来获取该数据并将其存储在请求中。即坚持

+0

这基本上回答了我的问题,谢谢! –

1

用户数据听起来像它应该生活在像一个UserProfile模型

+0

用户登录后会写入数据库,但如果用户没有登录,我仍然希望他们能够设置首选项,一旦他们最终登录,这些首选项将被写入到userprofile模型,所以问题是通过登录 –

+0

维护数据,但是你不知道它是同一个人还是同一浏览器的另一个用户。所以你很乐意认为这是同一个人? – second

+0

对于简单的显示设置是的,但设置数据库对象可以标记为“授权”,以便只有某些设置被写入cookie,同时所有设置都被写入登录会话。 –

0

其实,我觉得你的初始设计是有道理的。如果你想在登录/注销边界保存一些会话变量,你可以做这样的事情。

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 /注销。如果你委托给那些内置的视图,你可以轻松地包装它们。

from django.contrib.auth import views 

@persist_session_vars(['HTTP_REFERER']) 
def login(request, *args, **kwargs): 
    return views.login(request, *args, **kwargs) 
+0

这是一个很好的开始,但不是线程安全的!由于'persist_session_vars'实例化了一次,所有用户都在写同一个'session_backup'字典(甚至使它成为一个对象变量而不是class属性在这里不会改变) – vdboor

+0

当两个用户同时登录时,我会收到对方的会话变量。在你的例子中,你的统计数据将关闭。但是,为了坚持这种个人信息,它会在网站中引入巨大的安全/信息泄漏。 – vdboor

0

您可以创建一个表单Mixin,使您可以将表单值保留到用户的会话中(不需要它们被登录)。这对于诸如公共表格视图报表上的筛选/排序选项等内容很有用,您希望在其中刷新时保持其筛选器选项的持久性。

filter form screenshot

查看:

def list_states(request): 
    if request.method == 'GET': 
     form = StateListFilterForm().load_from_session(request.session) 
    elif request.method == 'POST': 
     form = StateListFilterForm(request.POST) 
     form.persist_to_session() 
    return render('forms/state_list.html', RequestContext(request, {'state_form': form}) 

形式:

class PersistableMixin: 
    def persist_to_session(form, session): 
     for key in form.fields.keys(): 
      val = getattr(form, 'cleaned_data', form.data).get(key, None) 
      if val: # will not store empty str values 
       session[key] = val 
     return True 

    def load_from_session(form, session): 
     for key in form.fields.keys(): 
      saved_val = session.get(key, '') 
      if saved_val: # will not load empty str values 
       form.fields[key].initial = saved_val 
     return form 


class StateListFilterForm(forms.Form, PersistableMixin): 
    states = forms.MultipleChoiceField(required=False, choices=US_STATES) 
3

登录时,Django的要求session.flush()session.cycle_key(),这使得从旧的会话确保没有保留。这是一项安全措施,可以防止会话修复漏洞。因此,在应用此解决方案时,请注意您想要保留哪些变量。

它要保持某种状态,您必须在登录发出后恢复该状态。

Chase Seibert的解决方案是一个很好的开始,由于该代码中的线程安全问题,它非常不安全。你可以在这里找到一个改进版本,它是安全的使用方法:

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

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 

并在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)), 
]