Aunque la respuesta de Harold funciona en este caso específico, puedo ver al menos dos problemas importantes con ella:
- Esta solución solo se puede utilizar con un motor de sesión de base de datos . En otras situaciones (caché, archivo, cookie)
Session
no se utilizaría el modelo.
- Cuando aumenta el número de sesiones y usuarios en la base de datos, esto se vuelve bastante ineficiente.
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 tu models.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 su MIDDLEWARE_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
- Tenga en cuenta las implicaciones en el rendimiento de almacenar un campo adicional para sus usuarios. El uso de la herencia del modelo agregará un extra
JOIN
. El uso de perfiles de usuario agregará una consulta adicional. Modificar directamente el User
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 la login
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()