由于Django不活动,如何终止会话?


93

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

  1. 当用户关闭浏览器时,会话期满。
  2. 闲置一段时间后,会话将过期。
  3. 检测会话由于不活动而过期的时间,并向用户显示适当的消息。
  4. 在闲置时间结束前几分钟警告用户即将到期的会话。除了警告,还为用户提供扩展会话的选项。
  5. 如果用户正在应用程序中从事长时间的业务活动,而该活动不涉及将请求发送到服务器,则会话一定不能超时。

阅读文档,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)。

任何见解将不胜感激。


3
+1提供了详细的解决方案
Don

有一个中间件可以满足您的需求。在githubpypi上
gbutler 2013年

1
“由于cookie的工作方式,SESSION_EXPIRE_AT_BROWSER_CLOSE和SESSION_COOKIE_AGE是互斥的,即cookie在浏览器关闭时或在指定的到期时间到期。如果使用了SESSION_COOKIE_AGE,并且用户在cookie过期之前关闭了浏览器,则cookie将保留并重新打开浏览器将允许用户(或其他任何人)进入系统而无需重新验证。” 如果我错了,请纠正我,但是在较新的Django版本中,这似乎不再正确了吗?(至少1.5分以上)
BotondBéres2014年

1
“ Django仅依靠存在的cookie来确定会话是否处于活动状态。它不检查与会话一起存储的会话到期日期。” 这不再是真的
knaperek '17

Answers:


44

这是一个主意...在该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,

2
@ notbad.jpeg:通常,“活动”很容易被欺骗。掌握会话并不断发送请求的人只是活跃的。
RemcoGerlich 2014年

这是一个很好的答案。中间件是Django开发中使用率极低的工具。
Jamie Counsell

30

我只是刚接触Django的新手。

如果登录的用户关闭浏览器或处于空闲状态(不活动超时)一段时间,我想使会话过期。当我用Google搜索找出答案时,首先出现了这个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,我没有检查其他浏览器。1.即使设置了SESSION_COOKIE_AGE,关闭浏览器时,会话也已过期。2.仅当我空闲超过10秒钟时,会话才过期。多亏了SESSION_SAVE_EVERY_REQUEST,无论何时发生新请求,它都可以保存会话并更新超时以到期

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

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

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

Django手册1.10

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


26

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

...还有一个附加要求:如果服务器没有响应或攻击者断开了互联网连接:它仍然应该过期。

Disclamer:我维护此应用程序。但是我已经看了很长时间了:)


1
很酷的应用程序-设计精美,构建精良。漂亮,干净的代码...谢谢。
nicorellius

如果用户在离开时关闭浏览器或选项卡(不注销),是否仍会强制用户注销?它可以处理这种情况吗?
Mehmet Kagan Kayaalp'Aug

这将由纯http会话cookie到期处理,不是吗?
jpic 2016年

10

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

SESSION_COOKIE_AGE = 600      #10 minutes.

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

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

2
SESSION_COOKIE_AGE = 600这将延长每次使用新页面或刷新页面的会话时间
Aseem '18

1
我确认仅进行设置SESSION_COOKIE_AGE就足够了,并且任何请求(发送会话cookie)都将自动刷新会话cookie到期。
bruno desthuilliers,


3

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

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和令牌时,

try:
    key = self.request.session['access_key']
except KeyError:
    age = self.request.session.get_expiry_age()
    if age > set_age:
        #redirect to login page
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.