如何由于 Django 中的不活动而使会话过期?

Posted

技术标签:

【中文标题】如何由于 Django 中的不活动而使会话过期?【英文标题】:How to expire session due to inactivity in Django? 【发布时间】:2011-03-02 17:39:48 【问题描述】:

我们的 Django 应用程序具有以下会话管理要求。

    当用户关闭浏览器时会话过期。 会话在一段时间不活动后过期。 检测会话何时因不活动而过期并向用户显示适当的消息。 在不活动期结束前几分钟警告用户即将到期的会话。除警告外,还为用户提供延长会话的选项。 如果用户在应用内处理不涉及向服务器发送请求的长时间业务活动,则会话不得超时。

在阅读了文档、Django代码和一些与此相关的博文之后,我想出了以下实现方法。

要求 1 通过将 SESSION_EXPIRE_AT_BROWSER_CLOSE 设置为 True,可以轻松实现此要求。

要求 2 我看到了一些使用 SESSION_COOKIE_AGE 设置会话到期期限的建议。但是这种方法存在以下问题。

会话总是在 SESSION_COOKIE_AGE 结束时到期,即使用户正在积极使用应用程序。 (这可以通过使用自定义中间件将每个请求的会话到期设置为 SESSION_COOKIE_AGE 或通过将 SESSION_SAVE_EVERY_REQUEST 设置为 true 来保存每个请求的会话来防止。但是由于使用了 SESSION_COOKIE_AGE,下一个问题是不可避免的。)

由于 cookie 的工作方式,SESSION_EXPIRE_AT_BROWSER_CLOSE 和 SESSION_COOKIE_AGE 是互斥的,即 cookie 在浏览器关闭或指定的到期时间到期。如果使用 SESSION_COOKIE_AGE 并且用户在 cookie 过期之前关闭浏览器,则 cookie 会被保留,重新打开浏览器将允许用户(或其他任何人)进入系统而无需重新验证。

Django 仅依靠存在的 cookie 来确定会话是否处于活动状态。它不会检查与会话一起存储的会话到期日期。

以下方法可用于实现此要求并解决上述问题。

不要设置 SESSION_COOKIE_AGE。 在每个请求上将会话的到期日期设置为“当前时间 + 非活动期”。 覆盖 SessionMiddleware 中的 process_request 并检查会话是否过期。如果会话已过期,则丢弃会话。

要求 3 当我们检测到会话已经过期时(在上面的自定义 SessionMiddleware 中),在请求上设置一个属性来指示会话过期。此属性可用于向用户显示适当的消息。

要求 4 使用 javascript 检测用户不活动,提供警告以及延长会话的选项。如果用户希望延长会话,请向服务器发送保活脉冲以延长会话。

要求 5 使用 JavaScript 检测用户活动(在长时间的业务操作期间)并向服务器发送保持活动脉冲以防止会话过期。


上面的实现方法看起来很复杂,我想知道是否有更简单的方法(特别是对于要求 2)。

任何见解都将受到高度赞赏。

【问题讨论】:

+1 提供详细的解决方案 有一个中间件可以满足你的需要。 on github 和 on pypi "由于 cookie 的工作方式,SESSION_EXPIRE_AT_BROWSER_CLOSE 和 SESSION_COOKIE_AGE 是互斥的,即 cookie 在浏览器关闭或指定的到期时间到期。如果使用 SESSION_COOKIE_AGE 并且用户在 cookie 之前关闭浏览器过期,cookie 被保留,重新打开浏览器将允许用户(或任何其他人)进入系统,而无需重新验证。”如果我错了,请纠正我,但在较新的 Django 版本中,这似乎不再适用? (至少 1.5+) "Django 仅依靠存在的 cookie 来确定会话是否处于活动状态。它不会检查与会话一起存储的会话到期日期。"这是not true了。 2020年,第二个答案应该是公认的。当前接受的一个增加了已经在 Django 上实现的东西的复杂性。 【参考方案1】:

我刚开始使用 Django。

如果登录的用户关闭浏览器或处于空闲状态(不活动超时)一段时间,我想让会话过期。当我用谷歌搜索它时,首先出现了这个 SOF 问题。多亏了不错的答案,我查找了资源以了解中间件在 Django 中的请求/响应周期中是如何工作的。很有帮助。

按照此处的最佳答案,我正准备将自定义中间件应用到我的代码中。但我还是有点怀疑,因为这里的最佳答案是在 2011 年编辑的。我花了更多时间从最近的搜索结果中搜索了一下,并想出了简单的方法。

SESSION_EXPIRE_AT_BROWSER_CLOSE = True
SESSION_COOKIE_AGE = 10 # set just 10 seconds to test
SESSION_SAVE_EVERY_REQUEST = True

除了chrome,我没有检查其他浏览器。

    即使设置了 SESSION_COOKIE_AGE,当我关闭浏览器时会话也会过期。 只有当我空闲超过 10 秒时,会话才过期。感谢 SESSION_SAVE_EVERY_REQUEST,每当您发生新请求时,它都会保存会话并更新超时以过期

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

请注意,会话 cookie 仅在创建或修改会话时发送。如果 SESSION_SAVE_EVERY_REQUEST 为 True,会话 cookie 将在每个请求上发送。

同样,会话 cookie 的过期部分在每次发送会话 cookie 时都会更新。

django manual 1.10

我只是留下答案,这样一些像我这样的 Django 新手就不会像我一样花太多时间来寻找解决方案。

【讨论】:

【参考方案2】:

这是一个想法...使用SESSION_EXPIRE_AT_BROWSER_CLOSE 设置在浏览器关闭时使会话过期。然后像这样在每个请求的会话中设置一个时间戳

request.session['last_activity'] = datetime.now()

并添加一个中间件来检测会话是否过期。像这样的东西应该处理整个过程......

from datetime import datetime
from django.http import HttpResponseRedirect

class SessionExpiredMiddleware:
    def process_request(request):
        last_activity = request.session['last_activity']
        now = datetime.now()

        if (now - last_activity).minutes > 10:
            # Do logout / expire session
            # and then...
            return HttpResponseRedirect("LOGIN_PAGE_URL")

        if not request.is_ajax():
            # don't set this for ajax requests or else your
            # expired session checks will keep the session from
            # expiring :)
            request.session['last_activity'] = now

然后,您只需创建一些 url 和视图,即可将有关会话到期的相关数据返回给 ajax 调用。

当用户选择“更新”会话时,可以这么说,您所要做的就是再次将requeset.session['last_activity'] 设置为当前时间

显然这段代码只是一个开始......但它应该让你走上正确的道路

【讨论】:

我只是对此持怀疑态度,但我不认为if not request.is_ajax() 是完全安全的。持有会话到期前欺骗/发送 ajax 调用并保持会话继续进行的人不能吗? @notbad.jpeg:一般来说,“活动”很容易被欺骗。掌握会话并继续发送请求的人处于活动状态。 这是一个很好的答案。中间件是 Django 开发中一个未被充分利用的工具。【参考方案3】:

django-session-security 就是这样做的...

...还有一个额外的要求:如果服务器没有响应或攻击者断开了互联网连接:无论如何它都应该过期。

免责声明:我维护这个应用程序。但是我一直在看这个帖子很长很长的时间:)

【讨论】:

很酷的应用程序 - 设计精美,制作精良。不错,干净的代码...谢谢。 如果用户在离开时关闭浏览器或标签页(未注销),是否仍会强制用户注销?它可以处理这种情况吗? 那将由纯http会话cookie到期处理,不是吗?【参考方案4】:

满足您的第二个要求的一种简单方法是将 settings.py 中的 SESSION_COOKIE_AGE 值设置为合适的秒数。例如:

SESSION_COOKIE_AGE = 600      #10 minutes.

但是,无论用户是否表现出某些活动,只要执行此操作,会话就会在 10 分钟后过期。为了解决这个问题,每次用户执行任何类型的请求时,都可以自动更新过期时间(再延长 10 分钟),使用以下语句:

request.session.set_expiry(request.session.get_expiry_age())

【讨论】:

SESSION_COOKIE_AGE = 600 这将随着每个新页面请求或页面刷新而延长会话年龄 我确认只要设置SESSION_COOKIE_AGE就足够了,任何请求(发送会话cookie)都会自动刷新会话cookie过期时间。【参考方案5】:

你也可以使用 *** build in functions

SESSION_SAVE_EVERY_REQUEST = True

【讨论】:

完全不相关 - 这是为了在每个请求上强制 session 保存(不是“会话 cookie 刷新”),而不检查会话是否已被修改。【参考方案6】:

在第一个请求中,您可以将会话到期设置为

self.request.session['access_key'] = access_key
self.request.session['access_token'] = access_token
self.request.session.set_expiry(set_age) #in seconds 

而当使用access_key和token时,

try:
    key = self.request.session['access_key']
except KeyError:
    age = self.request.session.get_expiry_age()
    if age > set_age:
        #redirect to login page

【讨论】:

【参考方案7】:

我正在使用 Django 3.2,我建议使用 django-auto-logout 包。

它允许活动时间和空闲时间会话控制。

在模板中,您可以将变量与 Javascript 一起使用。

【讨论】:

虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接答案可能会失效。 - From Review

以上是关于如何由于 Django 中的不活动而使会话过期?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 django 中设置会话永不过期

如何在 5 分钟内使 Django 会话过期?

如何在 10 分钟不活动后使 PHP 会话过期? [复制]

Laravel“页面由于不活动而过期”

由于不活动而导致会话超时[重复]

如果会话过期,如果没有活动并重定向登录页面,则烧瓶注销