Django4 中文入门教程 Django4.0 使用会话-在视图中使用会话

2024-02-25 开发教程 Django4 中文入门教程 匿名 5

当激活 ​SessionMiddleware ​后,每个 ​HttpRequest ​对象(任何 Django 视图函数的第一个参数) 将得到一个 ​session ​属性,该属性是一个类字典对象。

你可以在视图中任意位置读取它并写入 ​request.session​ 。你可以多次编辑它。

class backends.base.SessionBase

这是所有会话对象的基础类。它有以下标准字典方法:

  • __getitem__(key)​:fav_color = request.session['fav_color']
  • __setitem__(key, value)​:request.session['fav_color'] = 'blue'
  • __delitem__(key)​:del request.session['fav_color'] 。如果给定的 key 不在会话里,会引发 KeyError 。
  • __contains__(key)​:'fav_color' in request.session
  • get(key, default=None)​:fav_color = request.session.get('fav_color', 'red')
  • pop(key, default=__not_given)​:fav_color = request.session.pop('fav_color', 'blue')
  • keys()
  • items()
  • setdefault()
  • clear()

它也有以下方法:

  • flush()​:删除当前会话和会话cookie。如果你想确保早先的会话数据不能被用户的浏览器再次访问时,可以使用这个方法(比如,django.contrib.auth.logout() 函数调用它)。
  • set_test_cookie()​:设置一个测试cookie来确定用户的浏览器是否支持cookie。由于测试通过,你不需要在下一个页面请求时再次测试它。
  • test_cookie_worked()​:返回 True 或 False ,这取决于用户浏览器是否接受测试cookie。由于 cookie 的工作方式,你将必须在上一个独立的页面请求里调用 set_test_cookie() 。
  • delete_test_cookie()​:删除测试cookie。使用完测试cookie后用它来删除。
  • get_session_cookie_age()​:返回 session cookies的失效时间,以秒为单位。默认 SESSION_COOKIE_AGE 。
  • set_expiry(value)​:为会话设置过期时间。你可以传递很多不同值:如果 value 是整型,会话将在闲置数秒后过期。比如,调用 ​request.session.set_expiry(300)​ 会使得会话在5分钟后过期。如果 value 是一个 datetime 或 timedelta 对象,会话将在指定的 date/time 过期。注意,如果你正在使用 ​PickleSerializer ​,那么 datetime 和 timedelta 的值只能序列化。如果值为 0,则用户的会话 cookie 将在用户的 Web 浏览器关闭时过期。如果 value 是 None ,会话会恢复为全局会话过期策略。出于过期目的,读取会话不被视为活动。会话过期时间会在会话最后一次*修改*后开始计算。
  • get_expiry_age()​:返回该会话过期的秒数。对于没有自定义过期时间的会话(或者那些设置为浏览器关闭时过期的),这等同于 ​SESSION_COOKIE_AGE ​。这个函数接受两个可选的关键参数:​modification ​:会话的最后一次修改,当做一个 datetime 对象。默认是当前时间。​expiry ​:会话的过期信息,如一个 datetime 对象,整数(秒)或 None。默认为通过 set_expiry() 存储在会话中的值,或 None 。
  • get_expiry_date()​:返回该会话的到期日期。对于没有自定义过期的会话(或那些设置为在浏览器关闭时过期的会话),这将等于从现在开始的​SESSION_COOKIE_AGE​秒的日期。这个函数接受与 get_expiry_age() 相同的参数。
  • get_expire_at_browser_close()​:返回 True 或 False,具体取决于用户的 Web 浏览器关闭时用户的会话 cookie 是否会过期。
  • clear_expired()​:从会话存储中移除过期会话。这个类方法通过 ​clearsessions ​调用。
  • cycle_key()​:在保留当前会话的同时创建新的会话秘钥。​django.contrib.auth.login()​ 调用这个方法来防止会话固定攻击。

会话序列化

默认情况下,Django 序列会话数据使用 JSON 。你可以设置 ​SESSION_SERIALIZER ​来自定义会话序列化格式。即使在编写你自己的序列化程序中描述了警告,我们仍然强烈建议您坚持JSON序列化,尤其是在您使用cookie后端的情况下。
比如,如果你使用 pickle 来序列化会话数据,那么这里一个攻击场景。如果你正在使用 ​signed cookie session backend​ 并且攻击者已经知道了 ​SECRET_KEY ​(Django 并不存在会导致其泄露的固有漏洞),攻击者可以在会话里插入一个字符串,当 ​unpickled ​时,在服务器上执行任意代码。这样做的技术很简单,在互联网上也很容易获得。尽管cookie会话存储会对cookie数据进行签名防止篡改,但是泄露 ​SECRET_KEY ​会立即升级为远程代码执行的漏洞。

绑定序列化

class serializers.JSONSerializer

来自 ​django.core.signing​ 的JSON序列化器的装饰器。可以只序列化基本数据类型。
另外,因为JSON只支持字符串键,注意在 ​request.session​ 使用非字符串键会无法工作:

>>> # initial assignment
>>> request.session[0] = 'bar'
>>> # subsequent requests following serialization & deserialization
>>> # of session data
>>> request.session[0] # KeyError
>>> request.session['0']
'bar'

同样,数据也不能在JSON中编码,例如像 ​\xd9​ 这种非UTF8字节(会引发 ​UnicodeDecodeError ​)不会被存储。

class serializers.PickleSerializer

支持任何Python对象,但是,如上所述,如果 ​SECRET_KEY ​泄露,这会导致攻击者执行远程代码的漏洞。

编写自定义的序列化器

注意这与 ​PickleSerializer ​不同,​JSONSerializer ​不会处理任何Python数据类型。通常情况下,便利性和安全性之间要做出权衡取舍。如果你想在 JSON 支持的会话里存储任何高级数据类型(比如 ​datetime ​和 ​Decimal ​),你需要编写自己的序列化器(或者在存储这类值到 ​request.session​ 之前把它们转化JSON序列化类型)。虽然序列化这些值通常很简单( ​DjangoJSONEncoder ​或许有帮助),但编写一个解码器来可靠地取回你放进去的东西就更不容易了。 例如,你要返回一个字符串格式的 ​datetime ​,但这恰好与为 ​datetime​选择的格式相同,这样会有风险。
你的序列化类必须实现两个方法( ​dumps(self, obj) ​和​loads(self, data)​ ) 来分别进行序列化和反序列化会话数据字典。

会话对象指南

  • 在 ​request.session​ 上使用普通的 Python 字符串作为字典键。这更多的是一种惯例而不是硬性规定。
  • 以下划线开头的会话字典键保留给 Django 作内部使用。
  • 不要使用新对象覆盖 ​request.session​ ,不要访问或设置它的属性。像使用 Python 字典一样使用它。

示例

这个简单的视图将一个 ​has_commented ​变量在用户评论后设置为 ​True ​。它不允许用户发表评论多于一次:

def post_comment(request, new_comment):
if request.session.get('has_commented', False):
return HttpResponse("You've already commented.")
c = comments.Comment(comment=new_comment)
c.save()
request.session['has_commented'] = True
return HttpResponse('Thanks for your comment!')

这是一个记录站点成员的简单的视图。

def login(request):
m = Member.objects.get(username=request.POST['username'])
if m.check_password(request.POST['password']):
request.session['member_id'] = m.id
return HttpResponse("You're logged in.")
else:
return HttpResponse("Your username and password didn't match.")

这是记录成员退出的视图:

def logout(request):
try:
del request.session['member_id']
except KeyError:
pass
return HttpResponse("You're logged out.")

标准的 ​django.contrib.auth.logout()​ 函数实际上比这里要多一些来防止数据意外泄露。它调用 ​request.session​ 的 ​flush()​ 方法。我们使用这个例子作为示范如何使用会话对象,而不是完整的 ​logout()​ 实现。