Este é bizarro. Temos um site do Laravel e, nesse site, temos um timer por usuário, onde ele fica inativo por 15 minutos antes de ser inicializado.
Fazemos isso através de um timer que fica na página em um componente de reação, ele funciona como queremos, mas agora temos um novo problema: se um usuário estiver conectado e fechar a tampa do laptop, o site deve inicializá-lo . Bancos fazem isso, escolas e universidades fazem isso, sites do governo também fazem isso. Portanto, é possível, apenas não sei como.
Usamos soquetes da web, usando a biblioteca laravel-websockets e o Echo. O que eu gostaria de ver acontecer é:
- Depois de fechar o seu laptop, inicialize-o na tela de login. Portanto, da próxima vez que você abrir o laptop e efetuar o login, verá o navegador em que está na tela de login. Não precisa acontecer tão rapidamente, mas precisamos de uma maneira de enviar algo para o front-end, basicamente dizendo a eles para atualizar a página. Depois que a sessão é encerrada, definimos o tempo de vida da sessão em 15 minutos.
Algumas pessoas sugeriram em outras perguntas semelhantes:
- criar um manipulador de soquete da web personalizado
- Para comparar o cookie da sessão (no navegador) com o cookie do usuário no backend.
- Para ter um timer em funcionamento no front-end (o que fazemos, ele pára quando você fecha a tampa do laptop)
O mais popular parece estar usando soquetes da Web, ouvindo o usuário desconectá-lo e inicializá-lo, o que é bom e tudo mais, mas como enviar uma solicitação a um navegador suspenso para inicializá-lo?
Encontrei requestIdleCallback () Mas, novamente, acho que não é isso que desejo se já tiver um cronômetro de pulsação no site. Também não funciona em todos os navegadores.
Estou muito perdido aqui sobre como fazer isso, o exemplo que posso dar é:
Faça login no seu banco, coloque o computador no modo de espera, aguarde 15 a 20 minutos, desperte o computador, faça o login e veja se o seu banco agora está com você na tela de login. É isso que eu quero . Mas não sei como conseguir isso.
Você não pode enviar eventos para um navegador "adormecido" a partir do back-end e, embora sim, isso teria que ser uma solução de back-end, como você atualiza o front-end para que eles fiquem na tela de logout ao despertar o laptop ou computador?
fonte
Respostas:
ATUALIZAR
Quanto ao pedido WebSocket, eu suponho que você está usando Laravel WebSockets com
pusher
. O Pusher.io não oferece suporte ao tempo limite , você pode ler este artigo de suporte "Você planeja adicionar um recurso de tempo limite de conexão à biblioteca do cliente Canais pusher-js?" . Você pode testá-lo se ativar o modo de depuração do Laravel (APP_DEBUG=true
dentro.env
) e começarlaravel-websockets
no terminal (php artisan websockets:serve
) para poder ver os eventos do log de saída. Se você tentar fechar a tampa do laptop ou definir o computador para o modo de hibernação ( suspensão ), não verá nenhuma mensagem sobre esse evento. Você não pode fazer isso com opusher
protocolo. Existe o evento Presencemember_removed
, mas isso é acionado somente quando você fecha a guia ou faz o logout. É claro que você pode acionar o evento personalizado do cliente para o canal de presença, mas para isso, você também precisa de uma configuração de timer no lado do cliente e precisará criar um provedor de serviços para olaravel-websockets
servidor como este problema do github "Existe uma maneira de implementar webhooks? " .Isso acontece porque os temporizadores do cliente interrompem a execução na hibernação e, portanto, continuam de onde estavam antes. Mas se você usar uma variável de data para economizar tempo , essa variável não será atualizada quando o computador entrar em hibernação; assim, você saberá quando sair do modo de suspensão, verificando a variável de data que, comparada à hora atual, terá significantes diferença e será maior que o intervalo do temporizador.
Implementando lógica de tempo no cliente
Você também pode ver esta implementação nesta P / R relacionada : Algum navegador de área de trabalho pode detectar quando o computador sai do modo de suspensão?
Você pode configurar um cronômetro no cliente para executar a cada minuto. Não contaremos com o intervalo do cronômetro , mas esse cronômetro verificará uma variável de data do escopo externo se o período desde o último cronômetro for maior que
15
minutos; se for, isso significa que o navegador / JS interrompeu a execução por algum motivo , possivelmente hibernação do dispositivo ( suspensão ) e você redirecionou o usuário para a rota de logout.Exemplo de código do cliente JS:
Você pode verificar este exemplo simples, mas usando o. É melhor testá-lo em um laptop fechando a tampa e depois abri-la novamente após1
segundo temporizador com15
segundos logout aqui15 segundos acada minuto, porque se você tiver muitos programas em execução, o computador leva algum tempo para salvar o estado da memória, para concluir o modo de hibernação e interromper a execução.Exemplo de Trabalhadores da Web
Você pode até usar a API Web Workers para configurar um trabalhador da Web para ser muito mais seguro:
Código JS da página:
logoutWorker.js
Código do trabalhador da Web :Você também pode verificar o exemplo do Trabalhador da Web com o mesmo
15
timer de segundos aqui .fonte
clientSession
variável. Você pode verificar minha resposta novamente, eu até adicionei um exemplo de Web Worker.?v=1
o final.Primeiro, vamos expandir o motivo pelo qual os sites bancários desconectam você após 15 minutos sem atividade. É um requisito de PCI para segurança.
Requisito 8.1.8 do PCI-DSS :
Para conseguir isso, a solução é realmente muito mais primitiva do que você imagina . Ele não requer o uso de websockets nem sabe nada sobre o estado da máquina do cliente (inativo ou inativo ou não). Tudo o que é necessário é conhecer o tempo entre a solicitação atual usando essa sessão e a última solicitação usando a mesma sessão e garantir que não tenham mais de 15 minutos de intervalo. Se eles são, o usuário deve ser autenticado novamente. Caso contrário, você pode prosseguir com a solicitação.
A mensagem "sessão expirou"
Você provavelmente está se perguntando (se é assim tão simples) como a mensagem de tempo limite da sessão aparece quando você coloca o computador em suspensão e o ativa novamente. Esta parte é enganosamente simples.
Quando o computador é colocado no modo de suspensão, o navegador desconecta todas as conexões TCP / IP que, por sua vez, desligam o loop de eventos no mecanismo javascript. Portanto, os temporizadores não funcionam. Mas quando o navegador acorda novamente, ele tenta atualizar algumas coisas, incluindo a própria página. Portanto, quando a página é atualizada, a solicitação volta ao servidor que está chamando o servidor para exigir que o usuário seja autenticado novamente.
No entanto, isso não será responsável pelo modal de mensagens javascript (se é isso a que você está se referindo) que alguns sites bancários fazem. Além disso, nem todos os navegadores fazem uma atualização completa na página em todos os cenários. Portanto, outra abordagem pode ser adotada. Em vez de ter um cronômetro no navegador que expire após 15 minutos, você pode simplesmente armazenar o tempo de carregamento da página em javascript como um carimbo de data e hora e ter um intervalo de 1 segundo que compara esse carimbo de data / hora ao carimbo de data / hora atual do computador. Se eles tiverem mais de 15 minutos de intervalo, a sessão deverá ser encerrada.
Mesmo que o computador entre no modo de suspensão e o timer pare, a sessão acabará no tempo limite do servidor ( consulte a seção abaixo para obter detalhes ) e, quando o computador acordar novamente, o timer com um intervalo de 1 segundo será iniciado novamente, invocando o mensagem (como se o usuário atingisse o tempo limite enquanto o computador estava dormindo). O tempo perdido entre o momento em que o computador entrou no modo de suspensão e o tempo em que o computador acorda não importam, pois o registro de data e hora permanecerá na memória. A desconexão entre o cliente e o servidor não é importante porque eles não precisam comunicar essas informações para que a sessão seja encerrada corretamente no lado do servidor. O servidor pode fazer sua própria coleta de lixo e encerrar a sessão sem comunicação do cliente (ou seja, de forma assíncrona ).
Acredite ou não, os bancos não se importam com a atividade dentro do cliente. Eles se preocupam apenas com a atividade de solicitação ao servidor. Portanto, se você está se perguntando como eles mantêm a sessão ativa por mais de 15 minutos quando o usuário fica na mesma página por tanto tempo, eles simplesmente enviam uma solicitação AJAX em segundo plano para atualizar a sessão depois de perguntar ao usuário se ainda quer continuar.
Isso pode ser feito no mesmo
onload
retorno de chamada de evento que usamos anteriormente:Manipulando o encerramento da sessão no lado do servidor
Para lidar com o encerramento da sessão no lado do servidor, existem várias abordagens. Dependendo de qual você usar, você precisará de táticas diferentes. Um deles é usar o manipulador de sessões padrão do PHP e definir
session.max_lifetime
expirar após 15 minutos (isso exclui os dados da sessão inteiramente no lado do servidor, invalidando o cookie do cliente).Se você deixar o mecanismo padrão do manipulador de sessões fazer isso, poderá encontrar problemas, dependendo de qual manipulador for usado (arquivos, cache de memórias, redis, personalizado, etc.).
Com os arquivos (manipulador padrão), a coleta de lixo ocorre de duas maneiras:
session.max_lifetime
. O problema dessa abordagem é que, em sites com pouco tráfego, uma sessão pode ficar lá no servidor por um longo tempo, até que solicitações suficientes (dependendo dasession.gc_probability
pontuação) sejam necessárias para chamar o GC para limpar os arquivos da sessão.Com manipuladores baseados em memcached e redis, você não tem esse problema. Eles manipularão a limpeza da memória automaticamente. As sessões ainda podem permanecer na memória física por um tempo passado, mas o daemon não poderá acessá-las. Se você está preocupado com esse bit de segurança, pode criptografar suas sessões em repouso ou encontrar um armazenamento de chave / valor que tenha um mecanismo de GC de limpeza de memória mais rígido.
Com um manipulador de sessão personalizado, você precisará criar seu próprio mecanismo de GC. Por meio da
SessionHandlerInterface
implementação de umgc
método que fornece o intervalo de vida útil máximo da sessão, você seria responsável por verificar se a sessão passou a vida útil com base nesse intervalo e fazer sua coleta de lixo a partir daí.Você também pode configurar um ponto de extremidade separado que verifique a TTL da sessão (via solicitação AJAX assíncrona no lado do cliente) e envie uma resposta se a sessão expirar (forçando o javascript a autenticar novamente o usuário).
fonte
Portanto, o Idea está por trás de setInterval e Sockets, setInterval é suportado na maioria dos navegadores e javascript WbsocketApi é suportado em quase todos os navegadores.
Breve visão geral: setInterval () - esse comportamento da função segue quando o computador está no modo de suspensão / suspensão / hibernação, é pausado e, quando você está no modo de despertar, ele retoma a si próprio.
O código a seguir faz o seguinte, no início (talvez ao mesmo tempo, mas) ele inicia o php server_socket ouvindo as conexões,
que o javascript websocket api envia o carimbo de data / hora atual no carimbo de data / hora do Unix milissegundos a cada 2 segundos, você pode ter 1 segundo, depende de você.
depois que o soquete do servidor php está recebendo esse tempo e verifica se há algo como o tempo anterior para comparar, quando o código é instanciado pela primeira vez, o php não tem nada como o tempo anterior para compará-lo com o tempo que foi enviado pelo javascript websocket, então php não faz nada, mas economiza esse tempo na sessão chamada 'prev_time' e aguarda que outros dados de tempo sejam recebidos do soquete javascript, então aqui começa o segundo ciclo. quando o servidor php conecta novos dados de hora do javascript WebsocketApi, verifica se há algo parecido com o tempo anterior para comparar com esses dados de hora recém-recebidos, significa que o php verifica se a sessão chamada 'prev_time' existe, como estamos no segundo ciclo, o php descobre que existe, agarra seu valor e segue
$diff = $new_time - $prev_time
, $ diff será de 2 segundos ou 2000 milissegundos, porque lembre-se de que nosso ciclo setInterval acontece a cada 2 segundos e o formato de hora que estamos enviando é em milissegundos,do que o php verifica
if($diff<3000)
se a diferença é menor que 3000 se é que ele sabe que o usuário está ativo, novamente você pode manipular esses segundos como desejar, eu escolho 3000 porque a possível latência na rede é quase impossível, mas você sabe que sou sempre cauteloso quando trata-se de redes, então vamos continuar, quando o php determina que o usuário está ativo, o php redefine a sessão 'prev_time' com o valor$new_time
que foi recebido recentemente e, apenas para fins de teste, envia a mensagem de volta ao soquete javascript,mas se
$diff
for maior que 3000, significa que algo pausou nosso setInterval e só existe uma maneira de isso acontecer. Acho que você já sabe o que estou dizendo. Portanto, naelse
lógica de (if($diff<3000)
) você pode desconectar o usuário destruindo uma sessão específica e se quer redirecionar você pode enviar algum texto para o javacript socket e criar uma lógica que será executadawindow.location = "/login"
dependendo do texto, é isso aqui é o código:Primeiro, é o arquivo index.html apenas para carregar o javascript:
então é javascript, não é realmente muito bem codificado, mas você pode descobrir que ler comentários são importantes:
agora aqui é parte do código php, não se preocupe, há também o código completo, mas essa parte é realmente o que faz os trabalhos acima mencionados, você também encontrará outras funções, mas elas são para decodificar e trabalhar com soquetes javascript, portanto é a coisa certa aqui LEIA COMENTÁRIOS SÃO IMPORTANTES:
E aqui está o código completo do php:
NOTA LEIA: a
$new_time
variável está$jsTime
no códigocrie uma pasta e apenas copie e cole isso nos arquivos execute php socket com o comando: php -f server_socket.php vá para o localhost e teste-o no console aberto para ver as mensagens que dizem "você está ativo" ou "você não está ativo" (quando você vem do sono); sua execução acontecerá quando o usuário sair do modo de suspensão e não quando estiver no modo de espera, pois naquele momento tudo é armazenado em cache no arquivo de paginação (windows) ou no swap (linux)
fonte
Acho que tenho uma idéia, você discutiu muito sobre como o sistema de login / logout do banco funciona.
Caso 1: acesso da página da web ao usuário por tempo ilimitado, se o usuário estiver ativo
Sempre que o usuário estiver conectado, inicie um cronômetro no seu back-end (defina o limite de tempo que desejar), digamos 15 minutos. Agora o que isso significa? Isso significa que, se o usuário não realizar nenhuma atividade na página da Web, desconectá-lo-emos.
Agora, de frente, você pode enviar a atividade do usuário para o seu back-end (pode ser enviado usando soquete ou pesquisa longa), que basicamente redefinirá o timer e o usuário poderá usar a página da Web ativamente pelo tempo que desejar.
Se o usuário colocar o PC no modo de suspensão, o timer não será redefinido e você poderá invalidar a sessão assim que o timer terminar.
Se você deseja invalidar a sessão do usuário assim que colocar o PC no modo de suspensão, defina o limite do tempo de validação da sessão. Por exemplo, quando o usuário efetuar login, criaremos a sessão que será válida apenas por 10 segundos e, quando recebermos a solicitação de atividade do usuário, podemos redefinir o cronômetro e fornecer uma nova chave de sessão.
Espero que isso ajude você. Deixe-me saber se você tem alguma dúvida.
fonte
Eu escrevi um script para detectar se a máquina foi dormir. A idéia é que, quando a máquina estiver no modo de suspensão, todos os scripts serão interrompidos. Portanto, se acompanharmos o horário atual dentro de um timeInterval. Sempre que o timeInterval aciona o horário atual menos (-), o novo horário deve estar próximo o suficiente do timeInterval. Portanto, se quisermos verificar se o cronômetro ficou ocioso pelo tempo X, podemos verificar se a diferença horária é maior que X.
Exemplo de golpe verifica se o computador foi colocado no modo de suspensão por mais de 15s. Observe que, quando você coloca o computador em modo de espera, leva cerca de 15 segundos extras para imaginar todos os processadores. (Quando testado no MEU PC).
fonte
Eu implementei exatamente o mesmo requisito usando o AWS Cognito, com os Autorizadores Lambda e Redis, não posso compartilhar o código neste estágio, mas posso dizer tudo sobre como ele é implementado com esses componentes; os mesmos conceitos podem ser usados com outros não componentes da AWS.
Primeiramente, com a implementação de um logoff de inatividade, você precisará fazer isso do lado do servidor, como se alguém simplesmente desligasse o computador, o site front-end não o desconectaria. Eu usei o conceito de
ACTIVE
usuários. Quando os usuários são autenticados com êxito, eu armazeno com um TTL de 15 minutos em Redis uma entrada com uma chave dousername
valor &ACTIVE
(pode ser o nome de usuário + o ID da sessão se você deseja permitir várias sessões para um determinado usuário ao mesmo tempo).Nos meus Autorizadores personalizados, quando um usuário é
ACTIVE
e possui um Token válido, concedo a ele acesso ao recurso protegido E, o mais importante, faço outra inserção no Redis com ousername
&ACTIVE
.Sempre que o usuário efetua logout, faço logoff na minha solução de gerenciamento de identidade (Cognito) e os marca como
INACTIVE
. Observe que se um usuário nãoACTIVE
acessar a API dentro de 15 minutos, ele não terá mais uma entrada com o nome de usuário e não poderá mais acessar a API e precisará entrar novamente, para o qual será redirecionado.Há muitas coisas a considerar com essa abordagem: por um lado, os Autorizadores geralmente armazenam em cache os resultados por um certo período de tempo. Se, por exemplo, você armazena o resultado em cache por 5 minutos, por exemplo, o usuário pode ser desconectado em 10 minutos, como seu usuário. pode atingir o cache em vez do Autorizador, que não atualiza a
ACTIVE
entrada.Também é importante que você verifique se o que você usa para armazenar se um determinado usuário
ACTIVE
está altamente disponível e se recupera rapidamente em caso de falha.A abordagem de usar um armazenamento em cache dessa maneira é semelhante a como a invalidação de token é adaptada a protocolos de autorização sem estado, como o OAuth2.
Estamos usando essa abordagem há alguns meses, parece que funciona bem para nós, pode ser um requisito irritante de lidar, eu esperava no mundo da AWS que houvesse um pronto para usar da solução da caixa para isso, mas não havia o que falar.
fonte