如何确定用户在Django中何时有空闲超时?

如何确定用户在Django中何时有空闲超时?,django,session-timeout,Django,Session Timeout,我想审核用户在Django应用程序中遇到空闲超时的情况。换句话说,如果用户的会话cookie的过期日期超过settings.py中的会话\u cookie\u AGE,则用户将重定向到登录页面。当这种情况发生时,也应该进行审计。我所说的“审计”,是指一份记录应该写给我的人。审计表 目前,我已经配置了一些中间件来捕获这些事件。不幸的是,当用户被重定向到登录页面时,Django会生成一个新的cookie,因此我无法确定用户是否通过空闲超时或其他事件被带到登录页面 据我所知,我需要使用“django_

我想审核用户在Django应用程序中遇到空闲超时的情况。换句话说,如果用户的会话cookie的过期日期超过settings.py中的会话\u cookie\u AGE,则用户将重定向到登录页面。当这种情况发生时,也应该进行审计。我所说的“审计”,是指一份记录应该写给我的人。审计表

目前,我已经配置了一些中间件来捕获这些事件。不幸的是,当用户被重定向到登录页面时,Django会生成一个新的cookie,因此我无法确定用户是否通过空闲超时或其他事件被带到登录页面

据我所知,我需要使用“django_会话”表。但是,此表中的记录无法与该用户关联,因为当发生重定向时,cookie中的sessionid值会重置


我猜我不是第一个遇到这种困境的人。有人知道如何解决这个问题吗?

我不知道Django的情况,但是你能简单地创建一个非持久性cookie,它存储对站点上某个页面的最后访问时间(每次页面加载时更新cookie)

然后,在您的登录页面上,您可以检查您的用户是否有cookie,但没有会话,然后,您知道用户的会话可能已超时。由于您拥有最后一次访问站点页面的时间,您还可以根据会话的持续时间计算会话是否超时。

更新: 经过一点测试,我意识到下面的代码不能回答您的问题。尽管它可以工作,并且信号处理程序被调用,
prev\u session\u data
如果存在,将不包含任何有用的信息

首先,对sessions框架的内部了解:

  • 当新访问者请求应用程序URL时,会为他们生成一个新会话-此时,他们仍然是匿名的(
    request.user
    是AnonymousUser的一个实例)
  • 如果他们请求一个需要身份验证的视图,他们将被重定向到登录视图
  • 当请求登录视图时,它会在用户会话(
    SessionStore.\u session
    )中设置一个测试值;这会自动设置当前会话上的
    已访问
    已修改
    标志
  • 在上述请求的响应阶段,
    SessionMiddleware
    保存当前会话,有效地在
    django_session
    表中创建一个新的
    session
    实例(如果您使用的是默认数据库支持的会话,由
    django.contrib.sessions.backends.db
    提供)。新会话的id保存在
    settings.session\u COOKIE\u NAME
    COOKIE中
  • 当用户输入用户名和密码并提交表单时,将对其进行身份验证。如果身份验证成功,则调用
    django.contrib.auth
    中的
    login
    方法<代码>登录检查当前会话是否包含用户ID;如果是,并且ID与登录用户的ID相同,则调用SessionStore.cycle_key创建新会话密钥,同时保留会话数据。否则,将调用
    SessionStore.flush
    ,以删除所有数据并生成新会话。这两种方法都应该删除上一个会话(对于匿名用户),并调用
    SessionStore.create
    创建新会话
  • 此时,用户已通过身份验证,他们拥有一个新会话。他们的ID与用于对他们进行身份验证的后端一起保存在会话中。会话中间件将此数据保存到数据库,并将其新会话ID保存在
    设置中。会话\u COOKIE\u NAME
  • 因此,您可以看到,上一个解决方案的最大问题是,在调用
    create
    时(步骤5),上一个会话的ID早已消失。正如所述,这是因为一旦会话cookie过期,浏览器就会自动删除它

    在此基础上,我想我已经想出了另一种方法,它似乎满足了你的要求,尽管它的边缘仍然有点粗糙。基本上,我使用第二个长寿命的“审计”cookie来镜像会话ID,并使用一些中间件来检查cookie的存在。如有任何要求:

    • 如果审核cookie和会话cookie都不存在,则这可能是一个新用户
    • 如果审核cookie存在,但会话cookie不存在,则这可能是会话刚刚过期的用户
    • 如果两个cookie都存在并且具有相同的值,则这是一个活动会话
    以下是迄今为止的代码:

    sessionaudit.middleware.py:

    audit.models.py:

    settings.py:

    如果您正在使用它,您应该实现一个自定义注销视图,该视图在用户注销时显式删除审核cookie。此外,我建议使用django签名cookies中间件(但您可能已经在这样做了,不是吗?)

    旧的: 我认为您应该能够使用自定义会话后端来完成这项工作。以下是一些(未经测试的)示例代码:

    from django.contrib.sessions.backends.db import SessionStore as DBStore
    from django.db.models import signals
    
    session_created = signals.Signal(providing_args=['previous_session_key', 'new_session_key'])
    
    class SessionStore(DBStore):
        """
        Override the default database session store.
    
        The `create` method is called by the framework to:
        * Create a new session, if we have a new user
        * Generate a new session, if the current user's session has expired
    
        What we want to do is override this method, so we can send a signal
        whenever it is called.
        """
    
        def create(self):
            # Save the current session ID:
            prev_session_id = self.session_key
            # Call the superclass 'create' to create a new session:
            super(SessionStore, self).create()
            # We should have a new session - raise 'session_created' signal:
            session_created.send(self.__class__, previous_session_key=prev_session_id, new_session_key=self.session_key)
    
    将上面的代码保存为“customdb.py”,并将其添加到django项目中。在settings.py中,用上述文件的路径设置或替换“会话引擎”,例如:

    SESSION_ENGINE = 'yourproject.customdb'
    
    然后在中间件或models.py中,为“session_created”信号提供一个处理程序,如下所示:

    from django.contrib.sessions.models import Session
    from yourproject.customdb import session_created
    
    def audit_session_expire(sender, **kwargs):
        # remember that 'previous_session_key' can be None if we have a new user
        try:
            prev_session = Session.objects.get(kwargs['previous_session_key'])
            prev_session_data = prev_session.get_decoded()
            user_id = prev_session_data['_auth_user_id']
            # do something with the user_id
        except Session.DoesNotExist:
            # new user; do something else...
    
    session_created.connect(audit_session_expire)
    
    不要忘记将包含
    models.py
    的应用程序包含在
    已安装的应用程序中

    会话\u COOKIE\u AGE=1500#25分钟


    把它放在你的设置中,这样就可以解决这个问题并使会话过期。

    我是一名asp.net程序员,这种问题也会发生。(嗯,这是web开发,所以很相似)这就是我在asp.net中解决这个问题的方法,所以我建议这个答案。我仍然很困惑。我在哪里为audit\u session\u expire()注册接收器?
    audit\u session\u expire
    
    from django.contrib.sessions.backends.db import SessionStore as DBStore
    from django.db.models import signals
    
    session_created = signals.Signal(providing_args=['previous_session_key', 'new_session_key'])
    
    class SessionStore(DBStore):
        """
        Override the default database session store.
    
        The `create` method is called by the framework to:
        * Create a new session, if we have a new user
        * Generate a new session, if the current user's session has expired
    
        What we want to do is override this method, so we can send a signal
        whenever it is called.
        """
    
        def create(self):
            # Save the current session ID:
            prev_session_id = self.session_key
            # Call the superclass 'create' to create a new session:
            super(SessionStore, self).create()
            # We should have a new session - raise 'session_created' signal:
            session_created.send(self.__class__, previous_session_key=prev_session_id, new_session_key=self.session_key)
    
    SESSION_ENGINE = 'yourproject.customdb'
    
    from django.contrib.sessions.models import Session
    from yourproject.customdb import session_created
    
    def audit_session_expire(sender, **kwargs):
        # remember that 'previous_session_key' can be None if we have a new user
        try:
            prev_session = Session.objects.get(kwargs['previous_session_key'])
            prev_session_data = prev_session.get_decoded()
            user_id = prev_session_data['_auth_user_id']
            # do something with the user_id
        except Session.DoesNotExist:
            # new user; do something else...
    
    session_created.connect(audit_session_expire)