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

73

我正在使用这个来登录用户:

def login_backend(request):
    if request.method == 'POST':
        username = request.POST['username']
        password = request.POST['password']
        user = authenticate(username=username, password=password)
        if user is not None:
            login(request, user)
            request.session.set_expiry(300)
            return HttpResponseRedirect('/overview/')
        else:
            return HttpResponseRedirect('/login_backend/')
    else:
        return render_to_response('login_backend.html', context_instance=RequestContext(request))

我希望会话在5分钟后过期,因此我在上面的视图中添加了request.session.set_expiry(300)。但是会话从未过期。我做错了什么?


1
我已经回答过这个相同的问题了,这是链接http://stackoverflow.com/questions/14808238/middleware-is-not-work-expected/14808426#14808426。如果您有任何疑问,请告诉我。 - catherine
如果用户正在打开应用程序,我不希望会话过期。如果用户没有打开应用程序5分钟,我希望会话过期。 - pynovice
是的,没错,那就是我的答案。如果用户处于空闲状态,它会自动登出。你想让我在这里发布整个答案吗? - catherine
好的,请这样做。我会标记您的答案为正确的。 - pynovice
11个回答

79

如果用户正在打开应用程序,我不希望会话过期。如果用户没有打开应用程序5分钟,我希望会话过期。 - pynovice
请注意,我的中间件代码检查 request.user.is_authenticated()。 - dani herrera

41

升级Django 1.6

由于json可序列化的原因,下面的中间件代码在Django 1.6及以上版本中无法工作。为了使它可以在所有版本的Django中工作,请使用会话序列化程序。

settings.py

#Handle session is not Json Serializable
SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer'

上面的序列化器示例适用于Django 1.6,请搜索其他版本。谢谢...
创建middleware.py文件。
from datetime import datetime, timedelta
from django.conf import settings
from django.contrib import auth


class AutoLogout:
  def process_request(self, request):
    if not request.user.is_authenticated() :
      #Can't log out if not logged in
      return

    try:
      if datetime.now() - request.session['last_touch'] > timedelta( 0, settings.AUTO_LOGOUT_DELAY * 60, 0):
        auth.logout(request)
        del request.session['last_touch']
        return
    except KeyError:
      pass

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

更新您的 settings.py:

MIDDLEWARE_CLASSES = [
    .........................

    'app_name.middleware.AutoLogout', 
]

# Auto logout delay in minutes
AUTO_LOGOUT_DELAY = 5 #equivalent to 5 minutes

如果您设置会话序列化器,那么它将起作用,这将解决“<object>不可JSON序列化”的问题。 - catherine
或者你可以将datetime.now()更改为time.time()。对条件逻辑进行其他小的相关更改,它就可以在JSON序列化器上工作了。 - Hec
@Hec,没问题。会话序列化器是针对整体而不仅仅是日期时间的。谢谢... :) - catherine
1
@catherine,为什么你的代码与我在 我的答案 中提供的一年前的代码不同呢? - dani herrera
PickleSerializer很棒!(Django 1.8-相同路径) - madzohan
@catherine 我想开发自己的中间件,在用户不活动后注销用户;我试图理解你的代码,并使用新的Django编写中间件,但有一件事我不明白:request.session['last_touch']是什么时候第一次设置的? - SLATER

26

Django 1.9

编辑你的 settings.py 文件并添加以下内容:

# SESSION AGE 5 Minutes
SESSION_COOKIE_AGE = 5*60

@shacker 为什么不行呢?会丢失会话 cookie,不是吗? - jjmontes
2
在Django 1.10上完美运行 ;) 谢谢@Slipstream - Essex
1
适用于2.1版本。谢谢。 - Anthony Petrillo
我正在使用Django 3.1.5,直到我将SESSION_EXPIRE_AT_BROWSER_CLOSE = False设置为false,它才能正常工作。 - user3903448
有没有办法使这个触发器触发一个视图,将用户重定向到另一个页面,显示会话已过期? - PRA7H1K

18

根据你的项目,这可能不足够。

例如,如果用户花费6分钟填写表格并点击“保存”?浏览器将被重定向到登录页面,表单数据将丢失。

此外,如果用户在放开电脑时留下了打开浏览器页中的机密数据,则存在潜在的安全问题。

另外,如果用户阅读页面长达6分钟?然后在没有警告或任何扩展会话的方法的情况下注销他就不是很好了...

考虑到这些问题,您可能会发现django-session-security有用。


3
尝试了django-session-security模块,安装非常顺利,使用效果十分出色。感谢你们开发如此精妙的模块! - Saurabh Hirani
1
这个包在使用Django 2.2时对我很有效。文档中提供的指令需要进行一些必要的调整,因为它是针对Django pre-1.10编写的:
  • session_security.middleware.SessionSecurityMiddleware 被添加到 MIDDLEWARE 列表中,而不是 MIDDLEWARE_CLASSES,后者已经不存在了。
  • TEMPLATES"OPTIONS" 下的 "context_processors" 列表中,必须添加 "django.template.context_processors.request",而不是 "django.core.context_processors.request"
- Nano Tellez

9
我使用Django 2.1.7,最简单的方法是使Django会话过期:
  • first you need to install django-session-timeout with command:

    pip install django-session-timeout

  • then you need to update your SessionTimeoutMiddleware in settings.py

    MIDDLEWARE_CLASSES = [
         ...
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django_session_timeout.middleware.SessionTimeoutMiddleware',
         ...
    ]
    
  • at last you need to add SESSION_EXPIRE_SECONDS at the end of settings.py:

    SESSION_EXPIRE_SECONDS = 300 # 300 seconds = 5 minutes

默认情况下,会话将在会话开始X秒后过期。若要在上一次活动X秒后过期会话,请使用以下设置:

SESSION_EXPIRE_AFTER_LAST_ACTIVITY = True


1
很遗憾,awsebcli需要版本为1.11(但不是更高版本)的软件包'six',而django-session-timeout则需要1.12。因此对我来说不是一个选项 :( - Greg Holst
1
这个解决方案在我的Django 4.0项目中也适用。谢谢! - NullIsNot0

7

在调用login()函数之前,需要设置会话时间!

...
request.session.set_expiry(300)
login(request, user)
...     

2
似乎也可以按照其他的顺序工作——2022年,八年后…… - sebhaase

4

这对我很有用

settings.py

TIME= 240*60  //four hours  or your time
SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer'
SESSION_EXPIRE_AT_BROWSER_CLOSE= True
SESSION_COOKIE_AGE = TIME    //change expired session
SESSION_IDLE_TIMEOUT = TIME  //logout

middleware.py

from django.contrib.auth import logout
from django.contrib import messages
import datetime
from django.shortcuts import redirect

import settings

class SessionIdleTimeout:
    def process_request(self, request):
        if request.user.is_authenticated():
            current_datetime = datetime.datetime.now()
            if ('last_login' in request.session):
                last = (current_datetime - request.session['last_login']).seconds
                if last > settings.SESSION_IDLE_TIMEOUT:
                    logout(request, login.html)
            else:
                request.session['last_login'] = current_datetime
        return None

4
"settings.py" 文件中,将 SESSION_COOKIE_AGE 设置为默认的 1209600 秒(2 周),将 SESSION_SAVE_EVERY_REQUEST 设置为默认的 "False",如下所示:
# "settings.py"

SESSION_COOKIE_AGE = 180 # 3 minutes. "1209600(2 weeks)" by default

SESSION_SAVE_EVERY_REQUEST = True # "False" by default

如果 SESSION_SAVE_EVERY_REQUEST"True",那么用户在 SESSION_COOKIE_AGE 后将会被注销,并且每次访问Django网站时,会话过期时间都会被更新,因此用户不需要频繁登录
如果 SESSION_SAVE_EVERY_REQUEST"False",那么用户在 SESSION_COOKIE_AGE 后将会被注销,但每次访问Django网站时,会话过期时间不会被更新,因此用户需要频繁登录

4

为了使@jhonnylopez的答案保持最新,并在一段时间内不活动后注销,而不是在给定时间后注销:

settings.py

# Logout after a period of inactivity
INACTIVE_TIME = 15*60  # 15 minutes - or whatever period you think appropriate
SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer'
SESSION_EXPIRE_AT_BROWSER_CLOSE= True
SESSION_COOKIE_AGE = INACTIVE_TIME   # change expired session
SESSION_IDLE_TIMEOUT = INACTIVE_TIME  # logout

middleware.py

from django.contrib.auth import logout
import datetime

from settings import SESSION_IDLE_TIMEOUT


class SessionIdleTimeout(object):
    """Middle ware to ensure user gets logged out after defined period if inactvity."""
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        if request.user.is_authenticated:
            current_datetime = datetime.datetime.now()
            if 'last_active_time' in request.session:
                idle_period = (current_datetime - request.session['last_active_time']).seconds
                if idle_period > SESSION_IDLE_TIMEOUT:
                    logout(request, 'login.html')
            request.session['last_active_time'] = current_datetime

        response = self.get_response(request)
        return response

2
如果您只需要一个简单的5分钟过期时间,而不关心提示用户或允许他们延长会话,则django-session-timeout非常适用。
pip install django-session-timeout

SESSION_EXPIRE_SECONDS = 300  # 5 min
SESSION_EXPIRE_AFTER_LAST_ACTIVITY = True
SESSION_TIMEOUT_REDIRECT = '/accounts/logout/'  # redirect to whatever page

虽然这是一个快速解决会话在5分钟后过期的方法,并重定向到指定页面(注销),但它并不能解决本文中提到的其他问题,例如警告用户或给予延长会话的机会。如果最后一次活动设置为True,则会基于活动继续延长会话,但是如果他们正在阅读页面或填写需要超过5分钟的表单,则会将其注销,这可能会导致您的用户出现问题,具体取决于您的网站功能。
更多文档请参见:https://pypi.org/project/django-session-timeout/ 我尚未实施它,但计划下一步进行。如果需要提示用户并允许他们选择延长会话的附加功能,则一些其他帖子推荐使用django-session-security。
如果您不想走自己的中间件路线,那么这些都是很好的替代方案。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接