En mi aplicación Django, bajo ciertas condiciones, quiero poder forzar a los usuarios a cerrar sesión con un nombre de usuario. No necesariamente el usuario actual que está conectado, sino otro usuario. Entonces, el método de solicitud en mi vista no tiene ninguna información de sesión sobre el usuario que quiero cerrar sesión.
Estoy familiarizado con django.auth y con auth. método de cierre de sesión, pero toma la solicitud como argumento. ¿Existe una "forma de Django" para desconectar al usuario si todo lo que tengo es el nombre de usuario? ¿O tengo que lanzar mi propio SQL de cierre de sesión?
django
django-authentication
Sergey Golovchenko
fuente
fuente
user.session_set.all().delete()
. Descargo de responsabilidad: soy el autor de django-qsessions.Respuestas:
No creo que haya una forma autorizada de hacer esto en Django todavía.
La identificación de usuario se almacena en el objeto de sesión, pero está codificada. Desafortunadamente, eso significa que tendrá que recorrer todas las sesiones, decodificar y comparar ...
Dos pasos:
Primero elimine los objetos de sesión para su usuario objetivo. Si inician sesión desde varias computadoras, tendrán varios objetos de sesión.
from django.contrib.sessions.models import Session from django.contrib.auth.models import User # grab the user in question user = User.objects.get(username='johndoe') [s.delete() for s in Session.objects.all() if s.get_decoded().get('_auth_user_id') == user.id]
Luego, si es necesario, bloquéelos ...
user.is_active = False user.save()
fuente
Aunque la respuesta de Harold funciona en este caso específico, puedo ver al menos dos problemas importantes con ella:
Session
no se utilizaría el modelo.Para resolver esos problemas, le sugiero que adopte otro enfoque al problema. La idea es almacenar en algún lugar la fecha en que el usuario inició sesión para una sesión determinada y la última vez que solicitó que un usuario se desconectara.
Luego, cada vez que alguien acceda a su sitio, si la fecha de inicio de sesión es inferior a la fecha de cierre de sesión, puede forzar el cierre de sesión del usuario. Como dijo Dan, no existe una diferencia práctica entre cerrar la sesión de un usuario inmediatamente o en su próxima solicitud a su sitio.
Ahora, veamos una posible implementación de esta solución, para django 1.3b1 . En tres pasos:
1. almacenar en la sesión la última fecha de inicio de sesión
Afortunadamente, el sistema de autenticación de Django expone una señal llamada
user_logged_in
. Solo tienes que registrar esas señales y guardar la fecha actual en la sesión. En la parte inferior de tumodels.py
:from django.contrib.auth.signals import user_logged_in from datetime import datetime def update_session_last_login(sender, user=user, request=request, **kwargs): if request: request.session['LAST_LOGIN_DATE'] = datetime.now() user_logged_in.connect(update_session_last_login)
2. solicitar un cierre de sesión forzado para un usuario
Solo necesitamos agregar un campo y un método al
User
modelo. Hay varias formas de lograrlo ( perfiles de usuario , herencia de modelos , etc.), cada una con sus pros y sus contras.En aras de la simplicidad, usaré la herencia del modelo aquí, si opta por esta solución, no olvide escribir un backend de autenticación personalizado .
from django.contrib.auth.models import User from django.db import models from datetime import datetime class MyUser(User): force_logout_date = models.DateTimeField(null=True, blank=True) def force_logout(self): self.force_logout_date = datetime.now() self.save()
Luego, si desea forzar el cierre de sesión del usuario
johndoe
, solo tiene que:from myapp.models import MyUser MyUser.objects.get(username='johndoe').force_logout()
3. implementar la verificación de acceso
La mejor manera aquí es usar un middleware como sugirió Dan. Este middleware accederá
request.user
, por lo que debe colocarlo después'django.contrib.auth.middleware.AuthenticationMiddleware'
en suMIDDLEWARE_CLASSES
configuración.from django.contrib.auth import logout class ForceLogoutMiddleware(object): def process_request(self, request): if request.user.is_authenticated() and request.user.force_logout_date and \ request.session['LAST_LOGIN_DATE'] < request.user.force_logout_date: logout(request)
Deberias hacer eso.
Notas
JOIN
. El uso de perfiles de usuario agregará una consulta adicional. Modificar directamente elUser
es la mejor forma de rendimiento, pero sigue siendo un tema peludo .Si implementa esa solución en un sitio existente, probablemente tendrá algunos problemas con las sesiones existentes, que no tendrán la
'LAST_LOGIN_DATE'
clave. Puede adaptar un poco el código de middleware para tratar ese caso:from django.contrib.auth import logout class ForceLogoutMiddleware(object): def process_request(self, request): if request.user.is_authenticated() and request.user.force_logout_date and \ ( 'LAST_LOGIN_DATE' not in request.session or \ request.session['LAST_LOGIN_DATE'] < request.user.force_logout_date ): logout(request)
En django 1.2.x, no hay
user_logged_in
señal. Vuelve a anular lalogin
función:from django.contrib.auth import login as dj_login from datetime import datetime def login(request, user): dj_login(request, user) request.session['LAST_LOGIN_DATE'] = datetime.now()
fuente
Necesitaba algo similar en mi aplicación. En mi caso, si un usuario estaba inactivo, quería asegurarme de que si el usuario ya había iniciado sesión, se cerrará la sesión y no podrá seguir usando el sitio. Después de leer esta publicación, llegué a la siguiente solución:
from django.contrib.auth import logout class ActiveUserMiddleware(object): def process_request(self, request): if not request.user.is_authenticated: return if not request.user.is_active: logout(request)
Simplemente agregue este middleware en su configuración y listo. En el caso de cambiar las contraseñas, puede introducir un nuevo campo en el modelo de perfil de usuario que obligue a un usuario a cerrar la sesión, verificar el valor del campo en lugar de is_active anterior y también desarmar el campo cuando un usuario inicia sesión. Este último puede hacerse con la señal user_logged_in de Django .
fuente
if request.user.is_authenticated() and not request.user.is_active
Quizás, un poco de middleware que hace referencia a una lista de usuarios que se han visto obligados a cerrar la sesión. La próxima vez que el usuario intente hacer algo, cierre la sesión, lo redireccionará, etc.
A menos que, por supuesto, sea necesario cerrar la sesión inmediatamente. Pero, de nuevo, no se darían cuenta hasta que intentaran hacer una solicitud de todos modos, por lo que la solución anterior puede funcionar.
fuente
Esto es en respuesta a la consulta de Balon:
Sí, con alrededor de 140.000 sesiones para iterar, puedo ver por qué la respuesta de Harold puede no ser tan rápida como te gustaría.
La forma en que recomendaría es agregar un modelo cuyas únicas dos propiedades sean claves externas
User
ySession
objetos. Luego, agregue algún middleware que mantenga este modelo actualizado con las sesiones de usuario actuales. He usado este tipo de configuración antes; en mi caso, tomé prestado elsessionprofile
módulo de este sistema de inicio de sesión único para phpBB (consulte el código fuente en la carpeta "django / sessionprofile") y esto (creo) se adaptaría a sus necesidades.Lo que terminaría con una función de administración en algún lugar de su código como esta (asumiendo los mismos nombres de código y diseño que en el
sessionprofile
módulo vinculado anteriormente):from sessionprofile.models import SessionProfile from django.contrib.auth.models import User # Find all SessionProfile objects corresponding to a given username sessionProfiles = SessionProfile.objects.filter(user__username__exact='johndoe') # Delete all corresponding sessions [sp.session.delete() for sp in sessionProfiles]
(Creo que esto también eliminará los
SessionProfile
objetos, ya que por lo que recuerdo, el comportamiento predeterminado de Django cuandoForeignKey
se elimina un objeto al que hace referencia a es ponerlo en cascada y también eliminar el objeto que contiene elForeignKey
, pero si no, entonces es lo suficientemente trivial para eliminar el contenido desessionProfiles
cuando haya terminado.)fuente
También puede usar la función directa de django para hacer eso, se actualizará y cerrará todas las demás sesiones del usuario, excepto la actual.
from django.contrib.auth import update_session_auth_hash update_session_auth_hash(self.request, user)
Documentos para update_session_auth_hash aquí .
fuente
Como Tony Abou-Assaleh, también necesitaba cerrar la sesión de los usuarios que estaban inactivos, así que comencé implementando su solución. Después de un tiempo descubrí que el middleware está forzando una consulta de base de datos en todas las solicitudes (para verificar si el usuario estaba bloqueado) y, por lo tanto, afecta el rendimiento en las páginas que no requieren inicio de sesión.
Tengo un objeto de usuario personalizado y Django> = 1.7, así que lo que terminé haciendo es anular su
get_session_auth_hash
función para invalidar la sesión cuando el usuario está inactivo. Una posible implementación es:def get_session_auth_hash(self): if not self.is_active: return "inactive" return super(MyCustomUser, self).get_session_auth_hash()
Para que esto funcione,
django.contrib.auth.middleware.SessionAuthenticationMiddleware
debe estar ensettings.MIDDLEWARE_CLASSES
fuente
Como dijeron otros, puede iterar sobre todas las sesiones en DB, decodificarlas todas y eliminar las que pertenecen a ese usuario. Pero es lento, especialmente si su sitio tiene mucho tráfico y hay muchas sesiones.
Si necesita una solución más rápida, puede usar un backend de sesión que le permita consultar y obtener las sesiones de un usuario específico. En estos backends de sesión, Session tiene una clave externa para User, por lo que no necesita iterar sobre todos los objetos de sesión:
db
,cached_db
backends de sesión)db
backend de la sesión de django )Con estos backends, se pueden eliminar todas las sesiones de un usuario en una sola línea de código:
user.session_set.all().delete()
Descargo de responsabilidad: soy el autor de
django-qsessions
.fuente
desde django.contrib.sessions.models import Session
eliminar sesión de usuario
[s.delete() for s in Session.objects.all() if s.get_decoded().get('_auth_user_hash') == user.get_session_auth_hash()]
fuente
Incluso me enfrenté a este problema. Pocos spammers de la India siguen publicando sobre esas soluciones de Baba y Molvi for love.
Lo que hice es que en el momento de publicar acabo de insertar este código:
if request.user.is_active==False: return HttpResponse('You are banned on the site for spaming.')
fuente