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

我们的 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 _ EVY _ REQUEST 设置为 true 将每个请求的会话保存为 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 检测用户不活跃,提供警告,并提供扩展会话的选项。如果用户希望扩展,请向服务器发送一个 keep alive 脉冲以扩展会话。

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


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

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

64310 次浏览

Here's an idea... Expire the session on browser close with the SESSION_EXPIRE_AT_BROWSER_CLOSE setting. Then set a timestamp in the session on every request like so.

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

and add a middleware to detect if the session is expired. something like this should handle the whole process...

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

Then you just have to make some urls and views to return relevant data to the ajax calls regarding the session expiry.

when the user opts to "renew" the session, so to speak, all you have to do is set requeset.session['last_activity'] to the current time again

Obviously this code is only a start... but it should get you on the right path

django-session-security does just that...

... with an additional requirement: if the server doesn't respond or an attacker disconnected the internet connection: it should expire anyway.

Disclamer: I maintain this app. But I've been watching this thread for a very, very long time :)

also you can use stackoverflow build in functions

SESSION_SAVE_EVERY_REQUEST = True

In the first request, you can set the session expiry as

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

And when using the access_key and 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

One easy way to satisfy your second requirement would be to set SESSION_COOKIE_AGE value in settings.py to a suitable amount of seconds. For instance:

SESSION_COOKIE_AGE = 600      #10 minutes.

However, by only doing this the session will expire after 10 minutes whether or not the user exhibits some activity. To deal with this issue, expiration time can be automatically renewed (for another extra 10 minutes) every time the user performs any kind of request with the following sentence:

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

I am just pretty new to use Django.

I wanted to make session expire if logged user close browser or are in idle(inactivity timeout) for some amount of time. When I googled it to figure out, this SOF question came up first. Thanks to nice answer, I looked up resources to understand how middlewares works during request/response cycle in Django. It was very helpful.

I was about to apply custom middleware into my code following top answer in here. But I was still little bit suspicious because best answer in here was edited in 2011. I took more time to search little bit from recent search result and came up with simple way.

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

I didn't check other browsers but chrome.

  1. A session expired when I closed a browser even if SESSION_COOKIE_AGE set.
  2. Only when I was idle for more than 10 seconds, A session expired. Thanks to SESSION_SAVE_EVERY_REQUEST, whenever you occur new request, It saves the session and updates timeout to expire

To change this default behavior, set the SESSION_SAVE_EVERY_REQUEST setting to True. When set to True, Django will save the session to the database on every single request.

Note that the session cookie is only sent when a session has been created or modified. If SESSION_SAVE_EVERY_REQUEST is True, the session cookie will be sent on every request.

Similarly, the expires part of a session cookie is updated each time the session cookie is sent.

django manual 1.10

I just leave answer so that some people who is a kind of new in Django like me don't spend much time to find out solution as a way I did.

I'm using Django 3.2 and i recommend using the django-auto-logout package.

It allows active time and idle time session control.

In the template you can use variables together with Javascript.