如何使用会话

Django 提供了对匿名会话的全面支持。会话框架允许你在每个站点访问者的基础上存储和检索任意数据。它将数据存储在服务器端并抽象了 cookie 的发送和接收。Cookies 包含会话 ID – 而不是数据本身(除非你使用 基于 cookie 的后端)。

启用会话

会话是通过一段 中间件 实现的。

要启用会话功能,请执行以下操作:

  • 编辑 MIDDLEWARE 设置,并确保其中包含 'django.contrib.sessions.middleware.SessionMiddleware'。由 django-admin startproject 创建的默认 settings.py 已经激活了 SessionMiddleware。

如果你不想使用会话,你也可以从 MIDDLEWARE 中删除 SessionMiddleware 行,并从 INSTALLED_APPS 中删除 'django.contrib.sessions'。这将节省一些小额开销。

配置会话引擎

默认情况下,Django 将会话存储在数据库中(使用模型 django.contrib.sessions.models.Session)。尽管这很方便,但在某些设置中,将会话数据存储在其他地方可能更快,因此可以配置 Django 将会话数据存储在文件系统或缓存中。

使用基于数据库的会话

如果要使用基于数据库的会话,需要将 'django.contrib.sessions' 添加到你的 INSTALLED_APPS 设置中。

一旦配置完成,运行 manage.py migrate 来安装存储会话数据的单个数据库表。

使用缓存会话

为了获得更好的性能,你可能想要使用基于缓存的会话后端。

要使用 Django 的缓存系统存储会话数据,首先需要确保你已经配置了缓存;请查看 缓存文档 获取详细信息。

警告

只有当你使用 Memcached 或 Redis 缓存后端时,才应该使用基于缓存的会话。本地内存缓存后端不会保留数据足够长的时间,因此不是一个好选择,直接使用文件或数据库会话可能会更快,而不是通过文件或数据库缓存后端发送所有数据。此外,本地内存缓存后端不是多进程安全的,因此在生产环境中可能不是一个好选择。

如果在 CACHES 中定义了多个缓存,Django 将使用默认缓存。要使用另一个缓存,将 SESSION_CACHE_ALIAS 设置为该缓存的名称。

一旦配置了缓存,你需要在数据库支持的缓存和非持久性缓存之间进行选择。

缓存数据库后端(cached_db)使用直写式缓存 —— 会话写入按顺序同时应用于数据库和缓存。如果写入缓存失败,异常将通过 sessions logger 进行处理和记录,以避免导致其他成功的写入操作失败。

会话读取使用缓存,如果数据已从缓存中逐出,则使用数据库。要使用此后端,请将 SESSION_ENGINE 设置为 "django.contrib.sessions.backends.cached_db",并按照 使用基于数据库的会话 的配置说明进行操作。

缓存后端(cache)只在缓存中存储会话数据。这更快,因为它避免了数据库持久性,但你需要考虑当缓存数据被逐出时会发生什么情况。逐出可能发生在缓存填满或缓存服务器重新启动时,这将意味着会话数据丢失,包括用户的登录状态(用户会因此退出登录)。要使用此后端,请将 SESSION_ENGINE 设置为 "django.contrib.sessions.backends.cache"。

缓存后端可以通过使用具有适当配置的持久缓存(例如 Redis)来变成持久性的。但除非你的缓存明确配置为具有足够的持久性,否则选择缓存数据库后端是更安全的选择。这可以避免由于生产中不可靠的数据存储引起的边缘情况。

使用基于文件的会话

要使用基于文件的会话,请将 SESSION_ENGINE 设置为 "django.contrib.sessions.backends.file"。

你可能还想设置 SESSION_FILE_PATH 配置(默认为 tempfile.gettempdir() 的输出,很可能是 /tmp)来控制 Django 存储会话文件的位置。请确保你的 Web 服务器具有读写此位置的权限。

要使用基于 cookie 的会话,请将 SESSION_ENGINE 设置为 "django.contrib.sessions.backends.signed_cookies"。会话数据将使用 Django 的 加密签名 工具和 SECRET_KEY 设置存储。

注意

建议将 SESSION_COOKIE_HTTPONLY 设置为 True,以防止 JavaScript 访问存储的数据。

警告

会话数据被签名但未加密

在使用基于 cookie 的后端时,会话数据可以被客户端读取。

使用 MAC(消息认证码)用于保护数据免受客户端的更改,以便在被篡改时会话数据将被作废。如果存储 cookie 的客户端(例如你用户的浏览器)无法存储全部会话 cookie 并丢弃数据,会发生相同的作废。尽管 Django 对数据进行了压缩,但仍然完全有可能超过每个 cookie 的 常见限制 4096 字节。

不保证新鲜度

还要注意,虽然 MAC 可以保证数据的真实性(即它是由你的站点生成的,而不是其他人生成的),以及数据的完整性(即它都在那里且正确),但它不能保证新鲜度,也就是不能保证你被发送回客户端的是最后发送给客户端的内容。这意味着对于一些使用会话数据的情况,基于 cookie 的后端可能会使你容易受到 重放攻击 的威胁。与其他会话后端不同,这些后端会保留每个会话的服务器端记录,并在用户注销时使其失效,基于 cookie 的会话在用户注销时不会失效。因此,如果攻击者窃取了用户的 cookie,即使用户注销,他们仍然可以使用该 cookie 以该用户的身份登录。只有当 cookie 的寿命超过 SESSION_COOKIE_AGE 时,它们才会被检测为 ‘过时’。

性能

最后,cookie 的大小可能会影响你的网站的速度。

在视图中使用会话

当激活了 SessionMiddleware 时,每个 django.http.HttpRequest 对象 — 作为任何 Django 视图函数的第一个参数 — 都将具有一个 session 属性,它是一个类似于字典的对象。

你可以在视图的任何时候读取并写入 request.session。你可以多次编辑它。

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

__bool__()

版本变化:6.1

返回 is_empty 的相反结果,可以据此检查会话是否包含数据:

if request.session:
    # Session has data or a key
    pass

get(key, default=None)

aget(key, default=None)

异步版本: aget()

比如:fav_color = request.session.get('fav_color', 'red')

aset(key, value)

示例:await request.session.aset('fav_color', 'red')

update(dict)

aupdate(dict)

异步版本: aupdate()

示例:request.session.update({'fav_color': 'red'})

pop(key, default=__not_given)

apop(key, default=__not_given)

异步版本:apop()

比如:fav_color = request.session.pop('fav_color', 'blue')

keys()

akeys()

异步版本:akeys()

values()

avalues()

异步版本:avalues()

has_key(key)

ahas_key(key)

异步版本:ahas_key()

items()

aitems()

异步版本:aitems()

setdefault()

asetdefault()

异步版本:asetdefault()

clear()

它也有以下方法:

flush()

aflush()

异步版本:aflush()

删除当前会话数据,并删除会话 cookie。如果要确保浏览器无法再次访问先前的会话数据,可使用此方法;例如 django.contrib.auth.logout 就会调用它。

is_empty()

如果会话没有键且为空,返回 True。

异步版本:aset_test_cookie()

设置一个测试 cookie 来确定用户的浏览器是否支持 cookie。由于 cookie 的工作方式,你将无法在用户的下一个页面请求之前进行测试。有关更多信息,请参阅下面的 设置测试 cookie。

异步版本:atest_cookie_worked()

根据用户的浏览器是否接受测试 cookie,返回 True 或 False。由于 cookie 的工作方式,你必须在之前的单独页面请求中调用 set_test_cookie() 或 aset_test_cookie()。有关更多信息,请参阅下面的 设置测试 cookie。

异步版本:adelete_test_cookie()

删除测试 cookie。使用它来清理。

返回 SESSION_COOKIE_AGE 设置的值。这可以在自定义会话后端中进行覆盖。

set_expiry(value)

aset_expiry(value)

异步版本:aset_expiry()

设置会话的过期时间。你可以传递多种不同的值:

  • 如果 value 是一个整数,会话将在多少秒的不活动后过期。例如,调用 request.session.set_expiry(300) 会使会话在5分钟后过期。

  • 如果 value 是一个 datetime 或 timedelta 对象,会话将在特定的日期/时间过期。

  • 如果 value 是 0,用户的会话 cookie 将在用户关闭 Web 浏览器时过期。

  • 如果 value 是 None,会话将恢复使用全局会话过期策略。

读取会话不被视为用于过期目的的活动。会话过期是根据会话上次被 修改 的时间计算的。

get_expiry_age()

aget_expiry_age()

异步版本:aget_expiry_age()

返回会话过期前剩余的秒数。对于没有自定义过期时间的会话(或设置为在关闭浏览器时过期的会话),这将等于 SESSION_COOKIE_AGE。

此函数接受两个可选的关键字参数:

  • modification:会话的最后修改时间,作为一个 datetime.datetime 对象。默认为当前时间。
  • expiry:会话的过期信息,可以是 datetime.datetime 对象、int(以秒为单位)或 None。默认为 set_expiry/aset_expiry 存储在会话中的值(如果有),否则为 None。

注意

这个方法用于会话后端在保存会话时确定会话过期时间的秒数。它实际上并不打算在那种上下文之外使用。

尤其需要注意,只有同时具备正确的 modification 值,并且 expiry 被设置为 datetime 对象时,才可能据此确定会话剩余的有效时间。如果已经有 modification 值,直接计算过期时间会更直观:

expires_at = modification + timedelta(seconds=settings.SESSION_COOKIE_AGE)

get_expiry_date()

aget_expiry_date()

异步版本:aget_expiry_date()

返回会话将过期的日期。对于没有自定义过期时间的会话(或设置为在关闭浏览器时过期的会话),这将等于从现在开始 SESSION_COOKIE_AGE 秒后的日期。

此函数接受与 get_expiry_age 相同的关键字参数,并适用类似的用法注意事项。

get_expire_at_browser_close()

aget_expire_at_browser_close()

异步版本:aget_expire_at_browser_close()

根据用户的会话 cookie 是否在用户关闭 Web 浏览器时过期,返回 True 或 False。

clear_expired()

aclear_expired()

异步版本:aclear_expired()

从会话存储中删除已过期的会话。这个类方法由 clearsessions 调用。

cycle_key()

acycle_key()

异步版本:acycle_key()

在保留当前会话数据的同时创建新的会话键。django.contrib.auth.login 调用该方法,以减轻会话固定攻击。

会话序列化

默认情况下,Django使用 JSON 对会话数据进行序列化。你可以使用 SESSION_SERIALIZER 设置来自定义会话序列化格式。尽管在 自定义序列化器 中描述了一些注意事项,但我们强烈建议 特别是如果你使用的是基于 cookie 的后端,坚持使用 JSON 序列化。

例如,如果你使用 pickle 来序列化会话数据,以下是一个攻击场景。如果你使用的是 签名 cookie 会话后端 并且 SECRET_KEY (或 SECRET_KEY_FALLBACKS 的任何密钥)被攻击者知道(Django 本身没有内在的漏洞会导致其泄露),攻击者可以在他们的会话中插入一个字符串,当反序列化时,它在服务器上执行任意代码。这种技巧很简单,而且在互联网上很容易找到。尽管 cookie 会话存储对 cookie 中存储的数据进行了签名以防止篡改,但 SECRET_KEY 泄漏会立即升级为远程代码执行漏洞。

捆绑的序列化器

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 的数据,例如不能编码为 UTF-8 的字节,如 '\xd9' (会引发 UnicodeDecodeError),也无法存储。

请参阅 自定义序列化器 部分以获取关于 JSON 序列化的限制的更多详细信息。

编写你自己的序列化器

注意,django.contrib.sessions.serializers.JSONSerializer 无法处理任意 Python 数据类型。便利性和安全性之间通常需要权衡。如果要在基于 JSON 的会话中存储 datetime、Decimal 等更复杂的数据类型,需要编写自定义序列化器,或在存入 request.session 之前将其转换为可被 JSON 序列化的对象。这些值的序列化通常很直接,django.core.serializers.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.")

…而这一个则根据上面的 login() 方法将成员注销:

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

标准的 django.contrib.auth.logout 函数实际上做了更多的工作,以防止意外的数据泄露。它调用 request.session 的 backends.base.SessionBase.flush 方法。我们在这个示例中只是演示如何使用会话对象,而不是完整的 logout() 实现。

作为一种便利,Django 提供了一种测试用户浏览器是否接受 cookie 的方法。在视图中调用 request.session 的 backends.base.SessionBase.set_test_cookie 方法,并在后续的视图中调用 backends.base.SessionBase.test_cookie_worked — 不要在同一个视图调用中执行。

这种 set_test_cookie() 和 test_cookie_worked() 之间的分离方式是由于 cookie 的工作方式所必需的。当你设置一个 cookie 时,你实际上无法知道浏览器是否接受了它,直到浏览器的下一个请求。

验证测试 cookie 工作正常后,使用 delete_test_cookie 清除它是良好实践。

下面是一个典型用法示例:

from django.http import HttpResponse
from django.shortcuts import render


def login(request):
    if request.method == "POST":
        if request.session.test_cookie_worked():
            request.session.delete_test_cookie()
            return HttpResponse("You're logged in.")
        else:
            return HttpResponse("Please enable cookies and try again.")
    request.session.set_test_cookie()
    return render(request, "foo/login_form.html")

在视图外使用会话

注意

本节中的示例直接从 django.contrib.sessions.backends.db 后端导入 SessionStore 对象。在你自己的代码中,你应该考虑从由 SESSION_ENGINE 指定的会话引擎中导入 SessionStore,如下所示:

>>> from importlib import import_module
>>> from django.conf import settings
>>> SessionStore = import_module(settings.SESSION_ENGINE).SessionStore

在视图之外,有一个 API 可用于操作会话数据:

>>> from django.contrib.sessions.backends.db import SessionStore
>>> s = SessionStore()
>>> # stored as seconds since epoch since datetimes are not serializable in JSON.
>>> s["last_login"] = 1376587691
>>> s.create()
>>> s.session_key
'2b1189a188b44ad18c35e113ac6ceead'
>>> s = SessionStore(session_key="2b1189a188b44ad18c35e113ac6ceead")
>>> s["last_login"]
1376587691

SessionStore.create() 用于创建一个新的会话(即,一个未从会话存储加载并且 session_key=None 的会话)。save() 用于保存一个已存在的会话(即,从会话存储加载的会话)。在新会话上调用 save() 也可能有效,但有小概率生成与现有会话冲突的 session_key。create() 调用 save() 并循环直到生成一个未使用的 session_key。

如果你使用的是 django.contrib.sessions.backends.db 后端,每个会话都是一个普通的 Django 模型。Session 模型在 django/contrib/sessions/models.py 中定义。因为它是一个普通的模型,所以你可以使用普通的 Django 数据库 API 访问会话:

>>> from django.contrib.sessions.models import Session
>>> s = Session.objects.get(pk="2b1189a188b44ad18c35e113ac6ceead")
>>> s.expire_date
datetime.datetime(2005, 8, 20, 13, 35, 12)

注意,需要调用 get_decoded 才能取得会话字典,因为字典以编码后的格式存储:

>>> s.session_data
'KGRwMQpTJ19hdXRoX3VzZXJfaWQnCnAyCkkxCnMuMTExY2ZjODI2Yj...'
>>> s.get_decoded()
{'user_id': 42}

当会话被保存时

默认情况下,只有会话被修改时,Django 才会将其保存到会话数据库;也就是字典中的值被赋值或删除时:

# Session is modified.
request.session["foo"] = "bar"

# Session is modified.
del request.session["foo"]

# Session is modified.
request.session["foo"] = {}

# Gotcha: Session is NOT modified, because this alters
# request.session['foo'] instead of request.session.
request.session["foo"]["bar"] = "baz"

对于上例最后一种情况,可以将会话对象的 modified 属性设为 True,明确告知它已经被修改:

request.session.modified = True

要更改这个默认行为,将 SESSION_SAVE_EVERY_REQUEST 设置为 True。当设置为 True 时,Django 将在每个请求上将会话保存到数据库中。

请注意,只有在创建或修改会话时才会发送会话 cookie。如果 SESSION_SAVE_EVERY_REQUEST 是 True,则会在每个请求上发送会话 cookie。

类似地,会话 cookie 的 expires 部分在每次发送会话 cookie 时都会更新。

如果响应状态代码为 500,会话不会被保存。

浏览器生命周期会话与持久会话

你可以使用 SESSION_EXPIRE_AT_BROWSER_CLOSE 设置来控制会话框架是否使用浏览器生命周期会话还是持久会话。

默认情况下, SESSION_EXPIRE_AT_BROWSER_CLOSE 设置为 False,这意味着会话 cookie 将在用户的浏览器中存储,时间为 SESSION_COOKIE_AGE 的值。如果你不希望用户每次打开浏览器时都要登录,可以使用这个设置。

如果 SESSION_EXPIRE_AT_BROWSER_CLOSE 设置为 True,Django 将使用浏览器生命周期的 cookie — 即当用户关闭浏览器时立即过期的 cookie。如果你希望用户每次打开浏览器时都需要登录,可以使用这个设置。

这个设置是全局默认设置,可以通过在每个会话级别上显式调用 request.session 的 backends.base.SessionBase.set_expiry 方法来覆盖,如上面在 在视图中使用会话 部分所述。

注意

一些浏览器(例如Chrome)提供了允许用户在关闭并重新打开浏览器后继续浏览会话的设置。在某些情况下,这可能会影响到 SESSION_EXPIRE_AT_BROWSER_CLOSE 设置,并阻止会话在关闭浏览器时过期。在测试启用了 SESSION_EXPIRE_AT_BROWSER_CLOSE 设置的 Django 应用程序时,请注意这一点。

清除会话存储

随着用户在你的网站上创建新的会话,会话数据可能会在你的会话存储中累积。如果你使用的是数据库后端,django_session 数据库表会增长。如果你使用的是文件后端,你的临时目录会包含越来越多的文件。

要理解这个问题,考虑一下数据库后端的情况。当用户登录时,Django 会向 django_session 数据库表添加一行记录。每当会话数据发生更改时,Django 会更新此行记录。如果用户手动注销,Django 会删除这行记录。但如果用户没有注销,这行记录永远不会被删除。文件后端也有类似的过程。

Django 不会 自动清除过期的会话。因此,你需要定期清除过期的会话。Django 为此提供了一个清理管理命令:clearsessions。建议定期调用这个命令,例如作为每日的 cron 作业。

请注意,缓存后端不会受到这个问题的影响,因为缓存会自动删除过时的数据。同样,cookie 后端也不会受到影响,因为会话数据是由用户的浏览器存储的。

配置

一些 Django 设置 允许你控制会话的行为:

  • SESSION_CACHE_ALIAS
  • SESSION_COOKIE_AGE
  • SESSION_COOKIE_DOMAIN
  • SESSION_COOKIE_HTTPONLY
  • SESSION_COOKIE_NAME
  • SESSION_COOKIE_PATH
  • SESSION_COOKIE_SAMESITE
  • SESSION_COOKIE_SECURE
  • SESSION_ENGINE
  • SESSION_EXPIRE_AT_BROWSER_CLOSE
  • SESSION_FILE_PATH
  • SESSION_SAVE_EVERY_REQUEST
  • SESSION_SERIALIZER

会话安全

站点内的子域名可以在整个域上为客户端设置 cookie。如果允许来自不受信任用户控制的子域的 cookie,这将可能导致会话固定。

例如,攻击者可以登录到 good.example.com 并获得他们账户的有效会话。如果攻击者控制 bad.example.com,他们可以使用它来将他们的会话密钥发送给你,因为子域名被允许在 *.example.com 上设置 cookie。当你访问 good.example.com 时,你将以攻击者的身份登录,并可能不小心将你的敏感个人数据(如信用卡信息)输入到攻击者的帐户中。

另一个可能的攻击是,如果 good.example.com 将其 SESSION_COOKIE_DOMAIN 设置为 "example.com",这将导致该站点的会话 cookie 被发送到 bad.example.com。

技术细节

  • 当使用 django.contrib.sessions.serializers.JSONSerializer 时,会话字典接受任何可以进行 json 序列化的值。

  • 会话数据保存在名为 django_session 的数据库表中。

  • Django 只有它需要的时候才会发送 cookie 。如果你不想设置任何会话数据,它将不会发送会话 cookie 。

SessionStore 对象

在内部处理会话时,Django 使用相应会话引擎的会话存储对象。按照约定,会话存储对象类的名称为 SessionStore,并位于由 SESSION_ENGINE 指定的模块中。

Django 中所有可用的 SessionStore 子类都实现了以下数据操作方法:

  • exists()
  • create()
  • save()
  • delete()
  • load()
  • .SessionBase.clear_expired

通过使用 sync_to_async() 包装这些方法,提供了它们的异步接口。如果有原生的异步实现,可以直接实现它们:

  • aexists()
  • acreate()
  • asave()
  • adelete()
  • aload()
  • .SessionBase.aclear_expired

为了构建自定义的会话引擎或自定义现有的引擎,你可以创建一个继承自 backends.base.SessionBase 或任何其他现有的 SessionStore 类的新类。

你可以扩展会话引擎,但通常在使用数据库后端的会话引擎时需要额外的努力(详见下一节的详细信息)。

扩展数据库后端的会话引擎

可以通过继承 base_session.AbstractBaseSession 和任何 SessionStore 类来创建一个基于 Django 中包含的数据库后端会话引擎(即 db 和 cached_db )的自定义会话引擎。

AbstractBaseSession 和 BaseSessionManager 可以从 django.contrib.sessions.base_session 中导入,这样可以在 INSTALLED_APPS 中不包括 django.contrib.sessions 的情况下导入它们。

base_session.AbstractBaseSession

抽象的基础会话模型。

session_key

主键。该字段本身最多可包含 40 个字符。当前的实现生成一个 32 个字符的字符串(由数字和小写 ASCII 字母的随机序列组成)。

session_data

一个包含编码和序列化的会话字典的字符串。

expire_date

一个指示会话过期时间的日期时间。

已过期的会话对用户不可用,但它们仍然可能存储在数据库中,直到运行 clearsessions 管理命令。

get_session_store_class()

返回一个与此会话模型一起使用的会话存储类。

get_decoded()

返回解码后的会话数据。

解码由会话存储类执行。

你还可以通过继承 django.contrib.sessions.base_session.BaseSessionManager 来自定义模型管理器:

base_session.BaseSessionManager

encode(session_dict)

将给定的会话字典序列化并编码为字符串后返回。

编码由与模型类相关联的会话存储类执行。

save(session_key, session_dict, expire_date)

保存提供的会话键的会话数据,如果数据为空,则删除该会话。

通过覆盖下面描述的方法和属性来实现对 SessionStore 类的定制:

backends.db.SessionStore

实现基于数据库的会话存储。

get_model_class()

如果需要,覆盖此方法以返回自定义的会话模型。

create_model_instance(data)

返回一个新的会话模型对象实例,该对象表示当前会话状态。

覆盖此方法可以在数据保存到数据库之前修改会话模型数据。

backends.cached_db.SessionStore

实现了基于缓存的数据库后端的会话存储。

cache_key_prefix

用于构建缓存键字符串的会话键前缀。

例如

以下示例展示一个自定义数据库会话引擎:它增加一列来保存账户 ID,从而可以查询某个账户的所有活跃会话:

from django.contrib.sessions.backends.db import SessionStore as DBStore
from django.contrib.sessions.base_session import AbstractBaseSession
from django.db import models


class CustomSession(AbstractBaseSession):
    account_id = models.IntegerField(null=True, db_index=True)

    @classmethod
    def get_session_store_class(cls):
        return SessionStore


class SessionStore(DBStore):
    @classmethod
    def get_model_class(cls):
        return CustomSession

    def create_model_instance(self, data):
        obj = super().create_model_instance(data)
        try:
            account_id = int(data.get("_auth_user_id"))
        except (ValueError, TypeError):
            account_id = None
        obj.account_id = account_id
        return obj

如果从 Django 内置的 cached_db 会话存储迁移到基于 cached_db 的自定义存储,应覆盖缓存键前缀,以避免命名空间冲突:

class SessionStore(CachedDBStore):
    cache_key_prefix = "mysessions.custom_cached_db_backend"

    # ...

URL 中的会话 ID

Django 会话框架完全且仅基于 cookie。与 PHP 不同,它不会作为最后的手段将会话 ID 放在 URL 中。这是一个有意的设计决策。这种行为不仅会使 URL 变得难看,还会使你的网站容易受到通过”Referer”头部进行会话 ID 窃取的攻击。

来源与许可

原文:How to use sessions。Django Software Foundation 及贡献者版权所有。版本范围 Django 6.1。基于 stable/6.1.x 官方源文档与官方中文 PO 全文准备,补译未译段落,调整排版。适用 BSD 3-Clause;完整许可随交接包附于 django-LICENSE.txt。代码保持原文,仅核对结构与来源,未在本任务中运行。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容