¿Cómo caducar la sesión por inactividad en Django?

94

Nuestra aplicación Django tiene los siguientes requisitos de gestión de sesiones.

  1. Las sesiones caducan cuando el usuario cierra el navegador.
  2. Las sesiones caducan después de un período de inactividad.
  3. Detecta cuándo caduca una sesión debido a la inactividad y muestra el mensaje correspondiente al usuario.
  4. Advierta a los usuarios sobre el vencimiento de una sesión inminente unos minutos antes del final del período de inactividad. Junto con la advertencia, brinde a los usuarios una opción para extender su sesión.
  5. Si el usuario está trabajando en una actividad comercial prolongada dentro de la aplicación que no implica el envío de solicitudes al servidor, la sesión no debe expirar.

Después de leer la documentación, el código Django y algunas publicaciones de blog relacionadas con esto, se me ocurrió el siguiente enfoque de implementación.

Requisito 1
Este requisito se implementa fácilmente estableciendo SESSION_EXPIRE_AT_BROWSER_CLOSE en True.

Requisito 2
He visto algunas recomendaciones para usar SESSION_COOKIE_AGE para establecer el período de caducidad de la sesión. Pero este método tiene los siguientes problemas.

  • La sesión siempre expira al final de SESSION_COOKIE_AGE incluso si el usuario está usando la aplicación de forma activa. (Esto se puede evitar configurando el vencimiento de la sesión en SESSION_COOKIE_AGE en cada solicitud usando un middleware personalizado o guardando la sesión en cada solicitud configurando SESSION_SAVE_EVERY_REQUEST en verdadero. Pero el siguiente problema es inevitable debido al uso de SESSION_COOKIE_AGE).

  • Debido a la forma en que funcionan las cookies, SESSION_EXPIRE_AT_BROWSER_CLOSE y SESSION_COOKIE_AGE son mutuamente excluyentes, es decir, la cookie caduca al cerrar el navegador o en el tiempo de caducidad especificado. Si se utiliza SESSION_COOKIE_AGE y el usuario cierra el navegador antes de que caduque la cookie, la cookie se retiene y la reapertura del navegador permitirá que el usuario (o cualquier otra persona) ingrese al sistema sin volver a autenticarse.

  • Django se basa solo en la presencia de la cookie para determinar si la sesión está activa. No verifica la fecha de vencimiento de la sesión almacenada con la sesión.

El siguiente método podría utilizarse para implementar este requisito y solucionar los problemas mencionados anteriormente.

  • No establezca SESSION_COOKIE_AGE.
  • Establezca la fecha de vencimiento de la sesión en 'tiempo actual + período de inactividad' en cada solicitud.
  • Anule process_request en SessionMiddleware y compruebe la expiración de la sesión. Descarte la sesión si ha expirado.

Requisito 3
Cuando detectamos que la sesión ha expirado (en el SessionMiddleware personalizado anterior), establezca un atributo en la solicitud para indicar la expiración de la sesión. Este atributo se puede utilizar para mostrar un mensaje apropiado al usuario.

Requisito 4
Utilice JavaScript para detectar la inactividad del usuario, proporcionar la advertencia y también una opción para extender la sesión. Si el usuario desea extender la sesión, envíe un pulso de mantener vivo al servidor para extender la sesión.

Requisito 5
Utilice JavaScript para detectar la actividad del usuario (durante la operación comercial prolongada) y envíe impulsos de mantener vivo al servidor para evitar que la sesión expire.


El enfoque de implementación anterior parece muy elaborado y me preguntaba si podría haber un método más simple (especialmente para el Requisito 2).

Cualquier información será muy apreciada.

Akbar ibrahim
fuente
3
+1 por proporcionar una solución detallada
Don
Existe un middleware que puede hacer lo que necesite. en github y en pypi
gbutler
1
"Debido a la forma en que funcionan las cookies, SESSION_EXPIRE_AT_BROWSER_CLOSE y SESSION_COOKIE_AGE son mutuamente excluyentes, es decir, la cookie caduca al cerrar el navegador o en el tiempo de caducidad especificado. Si se usa SESSION_COOKIE_AGE y el usuario cierra el navegador antes de que caduque, la cookie se retiene y se vuelve a abrir el navegador permitirá que el usuario (o cualquier otra persona) ingrese al sistema sin volver a autenticarse ". Corrígeme si me equivoco, pero esto ya no parece ser cierto en las versiones más recientes de Django. (1.5+ al menos)
Botond Béres
1
"Django se basa únicamente en la presencia de la cookie para determinar si la sesión está activa. No verifica la fecha de caducidad de la sesión almacenada con la sesión". Esto ya no es cierto .
knaperek

Respuestas:

44

Aquí tienes una idea ... Expira la sesión al cerrar el navegador con la SESSION_EXPIRE_AT_BROWSER_CLOSEconfiguración. Luego, establezca una marca de tiempo en la sesión para cada solicitud como esa.

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

y agregue un middleware para detectar si la sesión ha expirado. algo como esto debería manejar todo el proceso ...

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

Luego, solo tiene que crear algunas direcciones URL y vistas para devolver datos relevantes a las llamadas ajax con respecto al vencimiento de la sesión.

cuando el usuario opta por "renovar" la sesión, por así decirlo, todo lo que tiene que hacer es requeset.session['last_activity']volver a configurar la hora actual

Obviamente, este código es solo el comienzo ... pero debería llevarlo por el camino correcto

Jiaaro
fuente
Solo estoy siendo escéptico aquí, pero no creo que if not request.is_ajax()sea ​​completamente seguro. ¿No puede alguien que se apodere de la sesión antes de la expiración de la suplantación de identidad / enviar una llamada ajax y mantener la sesión?
notbad.jpeg
2
@ notbad.jpeg: en general, la "actividad" se puede falsificar fácilmente. Alguien que se apodera de la sesión y sigue enviando solicitudes simplemente está activo.
RemcoGerlich
Esta es una respuesta genial. El middleware es una herramienta muy infrautilizada en el desarrollo de Django.
Jamie Counsell
31

Soy bastante nuevo en el uso de Django.

Quería hacer que la sesión expirara si el usuario registrado cierra el navegador o está inactivo (tiempo de espera de inactividad) durante algún tiempo. Cuando busqué en Google para averiguarlo, esta pregunta SOF surgió primero. Gracias a la buena respuesta, busqué recursos para comprender cómo funcionan los middlewares durante el ciclo de solicitud / respuesta en Django. Fue muy útil.

Estaba a punto de aplicar middleware personalizado en mi código siguiendo la respuesta principal aquí. Pero todavía sospechaba un poco porque la mejor respuesta aquí se editó en 2011. Me tomé más tiempo para buscar un poco en los resultados de búsqueda recientes y se me ocurrió una forma simple.

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

No revisé otros navegadores sino Chrome. 1. Una sesión expiró cuando cerré un navegador incluso si se configuró SESSION_COOKIE_AGE. 2. Solo cuando estuve inactivo durante más de 10 segundos, una sesión expiró. Gracias a SESSION_SAVE_EVERY_REQUEST, cada vez que se produce una nueva solicitud, guarda la sesión y actualiza el tiempo de espera para que expire

Para cambiar este comportamiento predeterminado, establezca la configuración SESSION_SAVE_EVERY_REQUEST en True. Cuando se establece en True, Django guardará la sesión en la base de datos en cada solicitud.

Tenga en cuenta que la cookie de sesión solo se envía cuando se ha creado o modificado una sesión. Si SESSION_SAVE_EVERY_REQUEST es True, la cookie de sesión se enviará en cada solicitud.

De manera similar, la parte de caducidad de una cookie de sesión se actualiza cada vez que se envía la cookie de sesión.

manual de django 1.10

Solo dejo la respuesta para que algunas personas que son algo nuevas en Django como yo no dediquen mucho tiempo a encontrar una solución como lo hice yo.

Jayground
fuente
26

django-session-security hace precisamente eso ...

... con un requisito adicional: si el servidor no responde o un atacante desconecta la conexión a Internet: debería caducar de todos modos.

Descargo de responsabilidad: mantengo esta aplicación. Pero he estado viendo este hilo durante mucho, mucho tiempo :)

jpic
fuente
1
aplicación genial, bien diseñada y bien construida. código agradable y limpio ... gracias.
nicorellius
Si el usuario cierra el navegador o la pestaña (sin cerrar la sesión) cuando se va, ¿todavía obliga al usuario a cerrar la sesión? ¿Maneja esta condición?
Mehmet Kagan Kayaalp
Eso se manejaría mediante el vencimiento de la cookie de sesión http puro, ¿no?
jpic
10

Una forma fácil de satisfacer su segundo requisito sería establecer el valor SESSION_COOKIE_AGE en settings.py en una cantidad adecuada de segundos. Por ejemplo:

SESSION_COOKIE_AGE = 600      #10 minutes.

Sin embargo, con solo hacer esto, la sesión expirará después de 10 minutos, ya sea que el usuario muestre alguna actividad o no. Para solucionar este problema, el tiempo de caducidad se puede renovar automáticamente (por otros 10 minutos más) cada vez que el usuario realiza algún tipo de solicitud con la siguiente frase:

request.session.set_expiry(request.session.get_expiry_age())
Fernando Martín
fuente
2
SESSION_COOKIE_AGE = 600 Esto extenderá la antigüedad de las sesiones con cada nueva solicitud de página o actualización de página
Aseem
1
Confirmo que solo la configuración SESSION_COOKIE_AGEes suficiente y que cualquier solicitud (enviar la cookie de sesión) actualizará automáticamente el vencimiento de la cookie de sesión.
bruno desthuilliers
3

también puede usar funciones integradas de stackoverflow

SESSION_SAVE_EVERY_REQUEST = True
mexekanez
fuente
Totalmente no relacionado: esto es para forzar un guardado de sesión (no "actualización de cookie de sesión") en cada solicitud sin verificar si la sesión ha sido modificada.
bruno desthuilliers
3

En la primera solicitud, puede establecer la caducidad de la sesión como

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

Y al usar la clave de acceso y el 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
tilaprimera
fuente