Como faço para determinar quando um usuário tem um tempo ocioso em Django?
-
22-07-2019 - |
Pergunta
Gostaria de auditoria quando um usuário tem experimentado um tempo ocioso na minha aplicação Django. Em outras palavras, se a data de validade do cookie de sessão do usuário excede o SESSION_COOKIE_AGE encontrado em settings.py, o usuário é redirecionado para a página de login. Quando isso ocorre, uma auditoria também deve ocorrer. Por "auditoria", quero dizer um registro deve ser escrito para minha mesa person.audit.
Atualmente, eu tenha configurado algum middleware para capturar esses eventos. Infelizmente, Django gera um novo cookie quando o usuário é redirecionado para a página de login, então eu não posso determinar se o usuário foi levado para a página de login via um tempo ocioso ou algum outro evento.
Do que eu posso dizer, eu precisaria de trabalho com o quadro de "django_session". No entanto, os registros nesta tabela não pode ser associado a esse usuário porque o valor sessionid no cookie é reposto quando o redirecionamento ocorre.
Eu estou supondo que eu não sou o primeiro a encontrar esse dilema. Alguém tem uma visão sobre como resolver o problema?
Solução
Update:
Depois de um pouco de testes, eu percebo que o código abaixo não responder à sua pergunta. Embora ele funciona, eo manipulador de sinal é chamado, prev_session_data
se existir, não contém nenhuma informação útil.
Primeiro, uma espiada no interior no quadro sessões:
- Quando um novo visitante solicita uma URL do aplicativo, uma nova sessão é gerado por eles - neste ponto, eles ainda são anônimos (
request.user
é uma instância de AnonymousUser) .
- Se eles pedem uma visão que requer autenticação, eles são redirecionados para a visão login.
- Quando a exibição de login é solicitado, ele define um valor de teste na sessão do usuário (
SessionStore._session
); este define automaticamente as bandeirasaccessed
emodified
na sessão atual. - Durante a fase de resposta do pedido acima, o
SessionMiddleware
salva a sessão atual, efetivamente criando uma nova instânciaSession
na tabela dedjango_session
(se você estiver usando as sessões de backup de banco de dados padrão, fornecidas peladjango.contrib.sessions.backends.db
). O id da nova sessão é salvo no cookiesettings.SESSION_COOKIE_NAME
. - Quando o usuário digita seu nome de usuário e senha e envia o formulário, eles são autenticados. Se a autenticação for bem sucedida, o método
login
dedjango.contrib.auth
é chamado.login
verifica se a sessão atual contém uma identificação de usuário; se isso acontecer, e o ID é o mesmo que o ID do usuário logado,SessionStore.cycle_key
é chamado para criar uma nova chave de sessão, mantendo os dados da sessão. Caso contrário,SessionStore.flush
é chamado, para remover todos os dados e gerar uma nova sessão. Ambos os métodos devem eliminar a sessão anterior (para o usuário anônimo), eSessionStore.create
chamada para criar uma nova sessão. - Neste ponto, o usuário é autenticado, e eles têm uma nova sessão. Sua ID é salvo na sessão, juntamente com a infra-estrutura usada para autenticá-los. O middleware sessão guarda estes dados para o banco de dados e salva seu novo ID de sessão em
settings.SESSION_COOKIE_NAME
.
Então você vê, o grande problema com a solução anterior é pela create
tempo é chamado (passo 5), ID da sessão anterior é muito longe. Como outros apontaram , isso acontece porque uma vez que o cookie de sessão expira, é silenciosamente eliminado pelo navegador.
Com base na Alex de Gaynor sugestão , Eu acho que eu vim com uma outra abordagem, que parece estar a fazer o que está pedindo, mas é ainda um pouco áspero em torno das bordas. Basicamente, eu uso um cookie segundo longa vida "auditoria", para espelhar o ID da sessão, e alguns middleware para verificar a presença desse cookie. Para qualquer pedido:
- Se nem o cookie de auditoria nem a exist cookie de sessão, este é provavelmente um novo usuário
- se o cookie existe auditoria, mas o cookie de sessão não faz, este é provavelmente um usuário cuja sessão caducado
- Se existirem as duas cookies, e têm o mesmo valor, esta é uma sessão ativa
Aqui está o código até agora:
sessionaudit.middleware.py :
from django.conf import settings
from django.db.models import signals
from django.utils.http import cookie_date
import time
session_expired = signals.Signal(providing_args=['previous_session_key'])
AUDIT_COOKIE_NAME = 'sessionaudit'
class SessionAuditMiddleware(object):
def process_request(self, request):
# The 'print' statements are helpful if you're using the development server
session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME, None)
audit_cookie = request.COOKIES.get(AUDIT_COOKIE_NAME, None)
if audit_cookie is None and session_key is None:
print "** Got new user **"
elif audit_cookie and session_key is None:
print "** User session expired, Session ID: %s **" % audit_cookie
session_expired.send(self.__class__, previous_session_key=audit_cookie)
elif audit_cookie == session_key:
print "** User session active, Session ID: %s **" % audit_cookie
def process_response(self, request, response):
if request.session.session_key:
audit_cookie = request.COOKIES.get(AUDIT_COOKIE_NAME, None)
if audit_cookie != request.session.session_key:
# New Session ID - update audit cookie:
max_age = 60 * 60 * 24 * 365 # 1 year
expires_time = time.time() + max_age
expires = cookie_date(expires_time)
response.set_cookie(
AUDIT_COOKIE_NAME,
request.session.session_key,
max_age=max_age,
expires=expires,
domain=settings.SESSION_COOKIE_DOMAIN,
path=settings.SESSION_COOKIE_PATH,
secure=settings.SESSION_COOKIE_SECURE or None
)
return response
audit.models.py :
from django.contrib.sessions.models import Session
from sessionaudit.middleware import session_expired
def audit_session_expire(sender, **kwargs):
try:
prev_session = Session.objects.get(session_key=kwargs['previous_session_key'])
prev_session_data = prev_session.get_decoded()
user_id = prev_session_data.get('_auth_user_id')
except Session.DoesNotExist:
pass
session_expired.connect(audit_session_expire)
settings.py :
MIDDLEWARE_CLASSES = (
...
'django.contrib.sessions.middleware.SessionMiddleware',
'sessionaudit.middleware.SessionAuditMiddleware',
...
)
INSTALLED_APPS = (
...
'django.contrib.sessions',
'audit',
...
)
Se você estiver usando isso, você deve implementar uma visão de logout personalizado, que exclui explicitamente o cookie de auditoria quando o usuário fizer fora. Além disso, eu sugiro usar assinou-os cookies do Django middleware (mas você provavelmente já está fazendo isso, não é?)
OLD:
Eu acho que você deve ser capaz de fazer isso usando um backend de sessão personalizado. Aqui estão algumas (não testado) código de exemplo:
from django.contrib.sessions.backends.db import SessionStore as DBStore
from django.db.models import signals
session_created = signals.Signal(providing_args=['previous_session_key', 'new_session_key'])
class SessionStore(DBStore):
"""
Override the default database session store.
The `create` method is called by the framework to:
* Create a new session, if we have a new user
* Generate a new session, if the current user's session has expired
What we want to do is override this method, so we can send a signal
whenever it is called.
"""
def create(self):
# Save the current session ID:
prev_session_id = self.session_key
# Call the superclass 'create' to create a new session:
super(SessionStore, self).create()
# We should have a new session - raise 'session_created' signal:
session_created.send(self.__class__, previous_session_key=prev_session_id, new_session_key=self.session_key)
Salvar o código acima de ums 'customdb.py' e acrescentar que para seu projeto Django. Em seu settings.py, conjunto ou substituir 'SESSION_ENGINE' com o caminho para o arquivo acima, por exemplo:.
SESSION_ENGINE = 'yourproject.customdb'
Então em seu middleware, ou models.py, fornecer um manipulador para o sinal 'session_created', assim:
from django.contrib.sessions.models import Session
from yourproject.customdb import session_created
def audit_session_expire(sender, **kwargs):
# remember that 'previous_session_key' can be None if we have a new user
try:
prev_session = Session.objects.get(kwargs['previous_session_key'])
prev_session_data = prev_session.get_decoded()
user_id = prev_session_data['_auth_user_id']
# do something with the user_id
except Session.DoesNotExist:
# new user; do something else...
session_created.connect(audit_session_expire)
Não se esqueça de incluir o aplicativo que contém o models.py
em INSTALLED_APPS
.
Outras dicas
SESSION_COOKIE_AGE = 1500 # 25 minutos
Coloque isso em suas configurações e que deve cuidar disso e expirar a sessão.
Eu não sei sobre Django, mas você pode, simplesmente crie um cookie não persistente, que armazena a última vez que o acesso a uma página em seu site (você atualizar o cookie em cada carregamento da página)
Então, em sua página de login, você pode verificar se o usuário tem o seu cookie, mas nenhuma sessão e, em seguida, você sabe que a sessão do usuário provavelmente expirou. Desde que você tem a hora do último acesso a uma página em seu site, você também pode calcular, em função da duração da sessão, se ele expirou.