Django4.0 使用会话-在视图中使用会话
当激活 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()
实现。
更多建议: