web-dev-qa-db-fra.com

Comment rechercher une session Django pour un utilisateur particulier?

J'écris une application où j'accéderai à la base de données depuis Django et depuis une application autonome. Les deux doivent effectuer une vérification de session et la session doit être la même pour les deux. Django a une authentification/vérification de session intégrée, qui est ce que j'utilise, maintenant je dois comprendre comment réutiliser la même session pour mon application autonome.

Ma question est de savoir comment puis-je rechercher une session_key pour un utilisateur particulier?

D'après ce qu'il en a l'air, il n'y a rien qui relie auth_user et Django_session

51
Sergey Golovchenko

Modifier la table Django_session Pour ajouter un user_id Explicite peut vous faciliter la vie. En supposant que vous fassiez cela (ou quelque chose de similaire), voici quatre approches pour fusionner les choses à votre goût:

Forkez le code Django.contrib.session. Je sais, je sais, c'est une chose horrible à suggérer. Mais ce n'est que 500 lignes, y compris tous les backends et moins les tests. C'est assez simple de pirater. Ce n'est le meilleur itinéraire que si vous allez réorganiser sérieusement les choses.

Si vous ne voulez pas bifurquer, vous pouvez essayer de vous connecter au signal Session.post_save Et y naviguer.

Ou vous pouvez MonkeyPatch contrib.session.models.Session.save(). Enveloppez simplement la méthode existante (ou créez-en une nouvelle), décomposez/synthétisez toutes les valeurs dont vous avez besoin, stockez-les dans vos nouveaux champs, puis super(Session, self).save().

Encore une autre façon de le faire est de mettre en 2 (oui, deux) classes middleware - une avant et une après SessionMiddleware dans vos settings.py fichier. Cela est dû à la façon dont le middleware est traité. Celui répertorié après SessionMiddleware obtiendra, sur la demande entrante, une demande avec la session déjà attachée. Celui répertorié ci-dessus peut effectuer n'importe quel traitement sur la réponse et/ou modifier/réenregistrer la session.

Nous avons utilisé une variante de cette dernière technique pour créer des pseudo-sessions pour les araignées des moteurs de recherche afin de leur donner un accès spécial à du matériel qui est normalement réservé aux membres. Nous détectons également les liens entrants où le champ REFERER provient du moteur de recherche associé et nous donnons à l'utilisateur un accès complet à cet article.

Mise à jour:

Ma réponse est maintenant assez ancienne, bien qu'elle soit encore généralement correcte. Voir la réponse beaucoup plus récente de @ Gavin_Ballard (29/09/2014) ci-dessous pour une autre approche de ce problème.

24
Peter Rowell

Cette réponse est publiée cinq ans après la question d'origine, mais ce fil SO est l'un des meilleurs résultats de Google lors de la recherche d'une solution à ce problème (et c'est toujours quelque chose qui n'est pas pris en charge) hors de la boîte avec Django).

J'ai une solution alternative pour le cas d'utilisation où vous n'êtes concerné que par les sessions utilisateur connectées, qui utilise un modèle UserSession supplémentaire pour mapper les utilisateurs à leurs sessions, quelque chose comme ceci:

from Django.conf import settings
from Django.db import models
from Django.contrib.sessions.models import Session

class UserSession(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL)
    session = models.ForeignKey(Session)  

Ensuite, vous pouvez simplement enregistrer une nouvelle instance de UserSession chaque fois qu'un utilisateur se connecte:

from Django.contrib.auth.signals import user_logged_in

def user_logged_in_handler(sender, request, user, **kwargs):
    UserSession.objects.get_or_create(user = user, session_id = request.session.session_key)

user_logged_in.connect(user_logged_in_handler)

Et enfin, lorsque vous souhaitez répertorier (et éventuellement effacer) les sessions pour un utilisateur particulier:

from .models import UserSession

def delete_user_sessions(user):
    user_sessions = UserSession.objects.filter(user = user)
    for user_session in user_sessions:
        user_session.session.delete()

Ce sont les écrous et les boulons de celui-ci, si vous souhaitez plus de détails, j'ai un blog post le couvrant.

44
Gavin Ballard

C'est quelque peu délicat à faire, car toutes les sessions ne sont pas nécessairement associées à un utilisateur authentifié; Le cadre de session de Django prend également en charge les sessions anonymes, et toute personne qui visite votre site aura une session, qu'elle soit connectée ou non.

Cela est encore plus compliqué par le fait que l'objet de session lui-même est sérialisé - puisque Django n'a aucun moyen de savoir exactement quelles données vous souhaitez stocker, il sérialise simplement le dictionnaire des données de session en une chaîne (en utilisant le module "pickle" standard de Python) et la place dans votre base de données.

Si vous disposez de la clé de session (qui sera envoyée par le navigateur de l'utilisateur en tant que valeur de cookie "sessionid"), le moyen le plus simple d'obtenir les données consiste simplement à interroger la table Session pour la session avec cette clé, qui renvoie une session objet. Vous pouvez ensuite appeler la méthode "get_decoded ()" de cet objet pour obtenir le dictionnaire des données de session. Si vous n'utilisez pas Django, vous pouvez consulter le code source (Django/contrib/sessions/models.py) pour voir comment les données de session sont désérialisées.

Si vous disposez de l'ID utilisateur, cependant, vous devrez parcourir tous les objets Session, en les désérialisant et en recherchant un qui a une clé nommée "_auth_user_id", et pour lequel la valeur de cette clé est l'ID utilisateur .

28
James Bennett

J'ai trouvé cet extrait de code

from Django.contrib.sessions.models import Session
from Django.contrib.auth.models import User

session_key = '8cae76c505f15432b48c8292a7dd0e54'

session = Session.objects.get(session_key=session_key)
uid = session.get_decoded().get('_auth_user_id')
user = User.objects.get(pk=uid)

print user.username, user.get_full_name(), user.email

ici http://scottbarnham.com/blog/2008/12/04/get-user-from-session-key-in-Django/

Je ne l'ai pas encore vérifié, mais cela semble assez simple.

26
michael

Peter Rowell, merci pour votre réponse. Ce fut une aide considérable. C'est ce que j'ai fait pour le faire fonctionner. Il ne fallait changer qu'un seul fichier dans djang.contrib.sessions.

Dans Django/contrib/sessions/models.py, ajoutez l'ID utilisateur à la table (ajoutez manuellement à la table DB ou supprimez la table et exécutez manage.py syncdb).

class Session(models.Model):

    ...

    user_id = models.IntegerField(_('user_id'), null=True)

    ...

    def save(self, *args, **kwargs):
        user_id = self.get_decoded().get('_auth_user_id')
        if ( user_id != None ):
            self.user_id = user_id

        # Call the "real" save() method.
        super(Session, self).save(*args, **kwargs)

Maintenant dans votre vue où vous vous connectez (si vous utilisez la connexion de base de Django, vous devrez la remplacer)

# On login, destroy all prev sessions
        # This disallows multiple logins from different browsers
        dbSessions = Session.objects.filter( user_id = request.user.id )
        for index, dbSession in enumerate( dbSessions ):
            if ( dbSession.session_key != request.session.session_key ):
                dbSession.delete()

Cela a fonctionné pour moi.

7
Joe

J'ai rencontré ce problème lorsque j'ai voulu expulser un spammeur. Il semble que définir leur compte sur "inactif" ne soit pas suffisant, car ils peuvent toujours participer à leur session précédente. Alors - comment supprimer une session pour un utilisateur spécifique, ou comment expirer délibérément une session pour un utilisateur spécifique?

La réponse est d'utiliser l'utilisateur last_login champ pour retrouver l'heure à laquelle la session a été désactivée, ce qui vous indique que le expire_date est deux semaines plus tard, ce qui vous permet d'effectuer un filtre utile sur la table des sessions:

from Django.contrib.sessions.models import Session
from Django.contrib.auth.models import User
from datetime import datetime
from dateutil.relativedelta import relativedelta

baduser = User.objects.get(username="whoever")     
two_weeks = relativedelta(weeks=2)
two_hours = relativedelta(hours=2)
expiry = baduser.last_login + two_weeks
sessions = Session.objects.filter(
    expire_date__gt=expiry - two_hours,
    expire_date__lt=expiry + two_hours
) 
print sessions.count() # hopefully a manageable number

for s in sessions:
    if s.get_decoded().get('_auth_user_id') == baduser.id:
        print(s)
        s.delete()
6
hwjp