Comment expirer une session en raison d'une inactivité dans Django?

94

Notre application Django a les exigences de gestion de session suivantes.

  1. Les sessions expirent lorsque l'utilisateur ferme le navigateur.
  2. Les sessions expirent après une période d'inactivité.
  3. Détecte lorsqu'une session expire en raison d'une inactivité et affiche le message approprié à l'utilisateur.
  4. Avertissez les utilisateurs de l'expiration imminente d'une session quelques minutes avant la fin de la période d'inactivité. En plus de l'avertissement, offrez aux utilisateurs la possibilité d'étendre leur session.
  5. Si l'utilisateur travaille sur une longue activité commerciale au sein de l'application qui n'implique pas l'envoi de demandes au serveur, la session ne doit pas expirer.

Après avoir lu la documentation, le code Django et quelques articles de blog liés à cela, j'ai proposé l'approche d'implémentation suivante.

Condition 1
Cette exigence est facilement mise en œuvre en définissant SESSION_EXPIRE_AT_BROWSER_CLOSE sur True.

Condition 2
J'ai vu quelques recommandations pour utiliser SESSION_COOKIE_AGE pour définir la période d'expiration de la session. Mais cette méthode présente les problèmes suivants.

  • La session expire toujours à la fin de SESSION_COOKIE_AGE même si l'utilisateur utilise activement l'application. (Cela peut être évité en définissant l'expiration de session sur SESSION_COOKIE_AGE à chaque demande à l'aide d'un middleware personnalisé ou en enregistrant la session à chaque demande en définissant SESSION_SAVE_EVERY_REQUEST sur true. Mais le problème suivant est inévitable en raison de l'utilisation de SESSION_COOKIE_AGE.)

  • En raison du fonctionnement des cookies, SESSION_EXPIRE_AT_BROWSER_CLOSE et SESSION_COOKIE_AGE s'excluent mutuellement, c'est-à-dire que le cookie expire à la fermeture du navigateur ou à l'heure d'expiration spécifiée. Si SESSION_COOKIE_AGE est utilisé et que l'utilisateur ferme le navigateur avant l'expiration du cookie, le cookie est conservé et la réouverture du navigateur permettra à l'utilisateur (ou à toute autre personne) d'accéder au système sans être ré-authentifié.

  • Django s'appuie uniquement sur la présence du cookie pour déterminer si la session est active. Il ne vérifie pas la date d'expiration de la session stockée avec la session.

La méthode suivante peut être utilisée pour implémenter cette exigence et pour contourner les problèmes mentionnés ci-dessus.

  • Ne définissez pas SESSION_COOKIE_AGE.
  • Définissez la date d'expiration de la session sur «heure actuelle + période d'inactivité» pour chaque demande.
  • Remplacez process_request dans SessionMiddleware et vérifiez l'expiration de la session. Ignorez la session si elle a expiré.

Condition 3
Lorsque nous détectons que la session a expiré (dans le SessionMiddleware personnalisé ci-dessus), définissez un attribut sur la demande pour indiquer l'expiration de la session. Cet attribut peut être utilisé pour afficher un message approprié à l'utilisateur.

Condition 4
Utilisez JavaScript pour détecter l'inactivité de l'utilisateur, fournissez l'avertissement et également une option pour prolonger la session. Si l'utilisateur souhaite étendre, envoyez une impulsion de maintien en vie au serveur pour étendre la session.

Condition 5
Utilisez JavaScript pour détecter l'activité de l'utilisateur (pendant la longue opération commerciale) et envoyer des impulsions de maintien en vie au serveur pour empêcher l'expiration de la session.


L'approche de mise en œuvre ci-dessus semble très élaborée et je me demandais s'il pourrait y avoir une méthode plus simple (en particulier pour l'exigence 2).

Toutes les idées seront très appréciées.

Akbar ibrahim
la source
3
+1 pour avoir fourni une solution détaillée
Don
Il existe un middleware qui peut faire ce dont vous avez besoin. sur github et sur pypi
gbutler
1
"En raison du fonctionnement des cookies, SESSION_EXPIRE_AT_BROWSER_CLOSE et SESSION_COOKIE_AGE sont mutuellement exclusifs, c'est-à-dire que le cookie expire à la fermeture du navigateur ou à l'heure d'expiration spécifiée. Si SESSION_COOKIE_AGE est utilisé et que l'utilisateur ferme le navigateur avant l'expiration du cookie, le cookie est conservé et rouvert le navigateur autorisera l'utilisateur (ou toute autre personne) à accéder au système sans être ré-authentifié. " Corrigez-moi si je me trompe, mais cela ne semble plus être vrai dans les nouvelles versions de Django? (1.5+ au moins)
Botond Béres
1
"Django se fie uniquement à la présence du cookie pour déterminer si la session est active. Il ne vérifie pas la date d'expiration de la session stockée avec la session." Ce n'est plus vrai .
knaperek

Réponses:

44

Voici une idée ... Expirez la session à la fermeture du navigateur avec le SESSION_EXPIRE_AT_BROWSER_CLOSEparamètre. Ensuite, définissez un horodatage dans la session pour chaque demande comme ceci.

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

et ajoutez un middleware pour détecter si la session a expiré. quelque chose comme ça devrait gérer tout le processus ...

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

Ensuite, il vous suffit de créer des URL et des vues pour renvoyer les données pertinentes aux appels ajax concernant l'expiration de la session.

lorsque l'utilisateur choisit de "renouveler" la session, pour ainsi dire, tout ce que vous avez à faire est de régler requeset.session['last_activity']à nouveau l'heure actuelle

Évidemment, ce code n'est qu'un début ... mais il devrait vous mettre sur la bonne voie

Jiaaro
la source
Je suis juste sceptique ici, mais je ne pense pas que ce if not request.is_ajax()soit complètement sûr. Est-ce que quelqu'un qui prend la main sur la session avant l'expiration ne peut pas usurper / envoyer un appel ajax et maintenir la session?
notbad.jpeg
2
@ notbad.jpeg: en général, "activité" est facilement usurpable. Quelqu'un qui met la main sur la session et continue d'envoyer des demandes est simplement actif.
RemcoGerlich
C'est une excellente réponse. L'intergiciel est un outil très sous-utilisé dans le développement de Django.
Jamie Counsell
31

Je suis juste assez nouveau pour utiliser Django.

Je voulais faire expirer la session si l'utilisateur connecté ferme le navigateur ou est inactif (délai d'inactivité) pendant un certain temps. Quand je l'ai recherché sur Google, cette question SOF a été soulevée en premier. Grâce à une belle réponse, j'ai recherché des ressources pour comprendre le fonctionnement des middlewares pendant le cycle de demande / réponse dans Django. Cela a été très utile.

J'étais sur le point d'appliquer un middleware personnalisé dans mon code en suivant la première réponse ici. Mais j'étais encore un peu méfiant car la meilleure réponse ici a été modifiée en 2011. J'ai pris plus de temps pour rechercher un peu à partir des résultats de recherche récents et j'ai trouvé un moyen simple.

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

Je n'ai pas vérifié d'autres navigateurs mais Chrome. 1. Une session a expiré lorsque j'ai fermé un navigateur même si SESSION_COOKIE_AGE est défini. 2. Seulement lorsque j'étais inactif pendant plus de 10 secondes, une session a expiré. Merci à SESSION_SAVE_EVERY_REQUEST, chaque fois que vous avez une nouvelle demande, il enregistre la session et met à jour le délai d'expiration

Pour modifier ce comportement par défaut, définissez le paramètre SESSION_SAVE_EVERY_REQUEST sur True. Lorsqu'il est défini sur True, Django enregistre la session dans la base de données à chaque requête.

Notez que le cookie de session n'est envoyé que lorsqu'une session a été créée ou modifiée. Si SESSION_SAVE_EVERY_REQUEST vaut True, le cookie de session sera envoyé à chaque demande.

De même, la partie expirant d'un cookie de session est mise à jour chaque fois que le cookie de session est envoyé.

django manuel 1.10

Je laisse juste la réponse pour que certaines personnes qui sont une sorte de nouveau dans Django comme moi ne passent pas beaucoup de temps à trouver une solution comme je l'ai fait.

Jayground
la source
26

django-session-security fait exactement cela ...

... avec une exigence supplémentaire: si le serveur ne répond pas ou si un attaquant a déconnecté la connexion Internet: elle devrait quand même expirer.

Disclamer: je maintiens cette application. Mais je regarde ce fil depuis très, très longtemps :)

jpic
la source
1
application cool - bien conçue et bien construite. code agréable et propre ... merci.
nicorellius
Si l'utilisateur ferme le navigateur ou l'onglet (sans se déconnecter) lorsqu'il quitte, cela force-t-il toujours l'utilisateur à se déconnecter? Gère-t-il cette condition?
Mehmet Kagan Kayaalp
Cela serait géré par l'expiration du cookie de session pur-http, n'est-ce pas?
jpic
10

Un moyen simple de répondre à votre deuxième exigence consiste à définir la valeur SESSION_COOKIE_AGE dans settings.py sur une durée appropriée en secondes. Par exemple:

SESSION_COOKIE_AGE = 600      #10 minutes.

Cependant, en ne faisant que cela, la session expirera au bout de 10 minutes, que l'utilisateur présente ou non une activité. Pour résoudre ce problème, le délai d'expiration peut être automatiquement renouvelé (pour 10 minutes supplémentaires) chaque fois que l'utilisateur effectue un type de demande avec la phrase suivante:

request.session.set_expiry(request.session.get_expiry_age())
Fernando Martin
la source
2
SESSION_COOKIE_AGE = 600 Cela prolongera l'âge des sessions à chaque nouvelle demande de page ou actualisation de page
Aseem
1
Je confirme que le réglage SESSION_COOKIE_AGEsuffit et que toute demande (l'envoi du cookie de session) actualisera automatiquement l'expiration du cookie de session.
bruno desthuilliers
3

vous pouvez également utiliser les fonctions intégrées de stackoverflow

SESSION_SAVE_EVERY_REQUEST = True
mexekanez
la source
Totalement indépendant - il s'agit de forcer une sauvegarde de session (et non un "rafraîchissement de cookie de session") à chaque demande sans vérifier si la session a été modifiée.
bruno desthuilliers
3

Dans la première demande, vous pouvez définir l'expiration de la session comme

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

Et lorsque vous utilisez la clé d'accès et le jeton,

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
la source