Desconectar um usuário de um site quando colocar o computador em suspensão

11

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:

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?

TheWebs
fonte
7
Basta definir a data de validade da sessão como "agora + 15 minutos" ... embora você ainda possa ver a última tela, não poderá atuar nessa sessão se o cookie tiver expirado. E a "tela de inicialização no logon" - tenho certeza de que existe uma configuração do sistema operacional que fará o logoff automático de um computador após um certo período de inatividade.
Lars Stegelitz
11
Você pode iniciar um cronômetro Javascript sempre que um site for carregado, com o mesmo tempo limite do cookie da sessão ... se o cronômetro "tocar", o usuário ficará inativo por tanto tempo. Você o desconectaria (chamada AJAX) e redirecionaria o navegador para a tela de login ou a tela "desculpe, desconectamos você devido à inatividade";)
Lars Stegelitz
11
Ok, eu fiz isso, estou adicionando a resposta agora.
Dato DT
11
então isso foi útil?
Dato DT
11
@DatoDT Não, um eu uso laravel, dois eu já uso soquetes laravel, conforme expresso no meu post de abertura, dois seu código é muito confuso, não OOP, não é testado e eu nunca o usaria. Eu estou procurando essencialmente uma solução laravel.
TheWebs

Respostas:

0

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çar laravel-websocketsno 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 o pusherprotocolo. 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 o laravel-websocketsservidor como este problema do github "Existe uma maneira de implementar webhooks? " .

Algumas pessoas sugeriram em outras perguntas semelhantes:

...

  • Para ter um timer em funcionamento no front-end (o que fazemos, ele pára quando você fecha a tampa do laptop )

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 15minutos; 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:

// Set a variable to check previous time
let clientSession = new Date;

// Setup the client session checking timer
let clientSessionTimer = setInterval(() => {
  const now = new Date;
  // Get how many seconds have passed since last check
  const secondsSpan = (now - clientSession) / 1000;

  // If the 1 minute timer has exceeded 15 minutes trigger logout and clear timer
  if (secondsSpan > (60 * 15)) {
    // For some reason JS halted execution, so we'll proceed with logging out
    clearInterval(clientSessionTimer);
    window.location.href = '/logout/session'
  } else {
    // The timer runs as it should, update the clientSession time
    clientSession = now;
  }

}, 1000 * 60);

Você pode verificar este exemplo simples, mas usando o 1segundo temporizador com 15segundos logout aqui . É melhor testá-lo em um laptop fechando a tampa e depois abri-la novamente após 15 segundos a cada 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:

const logoutWorker = new Worker('logoutWorker.js');
logoutWorker.onmessage = function (ev) {

  if (ev && ev.data === 'wakeup') {
    logoutWorker.terminate();
    // window.location.href = '/logout/session'
  } else {
    // The timer runs as it should, nothing to do
  }
}

logoutWorker.jsCódigo do trabalhador da Web :

let clientSession = new Date();

let clientSessionTimer = setInterval(() => {
  const now = new Date;
  const secondsSpan = (now - clientSession) / 1000;

  if (secondsSpan > 15) {
    postMessage('wakeup'); // Send a message wakeup to the worker page
    clearInterval(clientSessionTimer); // Clear the timer
  } else {
    clientSession = now; // Update the clientSession timer variable
    postMessage('update'); // And post a message to the page ONLY IF needed
  }
}, 1000);

Você também pode verificar o exemplo do Trabalhador da Web com o mesmo 15timer de segundos aqui .

Christos Lytras
fonte
Isso faz o logout após 15 segundos, não importa o que aconteça, não tendo certeza de que é isso que ele está procurando.
Islam Elshobokshy 16/03
@IslamElshobokshy você está certo, obrigado pela captura. Esqueci de atualizar a clientSessionvariável. Você pode verificar minha resposta novamente, eu até adicionei um exemplo de Web Worker.
Christos Lytras 16/03
Atualize os exemplos de amostra que ainda não funcionam. A primeira desconecta você após 15 segundos, não importa o quê. O segundo nunca desconecta você.
Islam Elshobokshy 16/03
@IslamElshobokshy Atualizei-o, é claro. Antes, havia um problema e agora funciona como esperado. Atualize a página se você não tiver ou talvez adicione um parâmetro como ?v=1o final.
Christos Lytras 16/03
11
Grande Solution + 3
AmerllicA
0

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 :

8.1.8 Se uma sessão estiver ociosa por mais de 15 minutos, exija que o usuário seja autenticado novamente para reativar o terminal ou a sessão.

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.

window.onload = function() {

    sessionStart = Date.now();
    timer = setInterval(function() {
        if (Date.now() - sessionStart > 15 * 60 * 1000) {
            clearTimeout(timer);
            alert("Session Timed out!");
            window.location = "http://www.example.com/login";
        }
    }, 1000);


};

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 onloadretorno de chamada de evento que usamos anteriormente:

window.onload = function() {

    sessionStart = Date.now();
    timer = setInterval(function() {
        if (Date.now() - sessionStart > 10 * 60 * 1000) {
           if (confirm("Your session is about to timeout. Do you wish to continue?")) {
                // send ajax request to refresh session TTL here
                // reset the timer
                sessionStart = Date.now();
            }
        } else if (Date.now() - sessionStart > 15 * 60 * 1000) {
            clearTimeout(timer);
            alert("Session Timed out!");
            window.location = "http://www.example.com/login";
        }
    }, 1000);


};

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_lifetimeexpirar 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:

  • A maioria dos sistemas baseados no Debian faz seu próprio GC através de um trabalho cron (o que funciona muito bem para o seu cenário)
  • Outras distribuições permitem que o mecanismo GC padrão do PHP lide com isso, que é baseado em um resultado probabilístico de cada solicitação de entrada no PHP que verifica os arquivos mtime nos arquivos de sessão e os exclui 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 da session.gc_probabilitypontuaçã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 SessionHandlerInterfaceimplementação de um gcmé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).

xerife
fonte
0

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_timeque foi recebido recentemente e, apenas para fins de teste, envia a mensagem de volta ao soquete javascript,

mas se $difffor 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, na elseló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á executada window.location = "/login"dependendo do texto, é isso aqui é o código:

Primeiro, é o arquivo index.html apenas para carregar o javascript:

<html>
    <body>
        <div id="printer"></div>
        <script src="javascript_client_socket.js"></script>
    </body>
</html>

então é javascript, não é realmente muito bem codificado, mas você pode descobrir que ler comentários são importantes:

var socket = new WebSocket('ws://localhost:34237'); // connecting to socket
    // Open the socket
socket.onopen = function(event) { // detecting when connection is established
        setInterval(function(){ //seting interval for 2 seconds
            var date = new Date(); //grabing current date
            var nowtime = Date.parse(date); // parisng it in miliseconds
            var msg = 'I am the client.'; //jsut testing message


            // Send an initial message
            socket.send(nowtime); //sending the time to php socket
    },2000);

};


// Listen for messages
socket.onmessage = function(event) { //print text which will be sent by php socket 
    console.log('php: ' + event.data);
};

// Listen for socket closes
socket.onclose = function(event) {
    console.log('Client notified socket has closed', event);
};

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:

<?php 
            $decoded_data = unmask($data /* $data is actual data received from javascript socket */); //grabbing data and unmasking it | unmasking is for javascript sockets don't mind this
            print("< ".$decoded_data."\n");
            $response = strrev($decoded_data);
            $jsTime = (int) $decoded_data; /* time sent by javascript in MILISECONDS IN UNIX FORMAT  */
            if (isset($_SESSION['prev_time'])) { /** check if we have stored previous time in the session */
               $prev_time = (int) $_SESSION['prev_time']; /** grabbing the previous time from session */
               $diff = $jsTime-$prev_time; /** getting the difference newly sent time and previous time by subtracting */
               print("$jsTime - $prev_time = $diff"); /** printing the difference */
               if($diff<3000){ /** checking if difference is less than 3 second if it is it means pc was not at sleep
                               *** you can manipulate and have for example 1 second = 1000ms */
                    socket_write($client,encode("You are active! your pc is awakend"));
                    $_SESSION['prev_time'] = $jsTime; /** saving newly sent time as previous time for future testing whcih will happen in two seconds in our case*/
                }else { /** if it is more than 3 seconds it means that javascript setInterval function was paused and resumed after 3 seconds 
                            ** So it means that it was at sleep because when your PC is at sleep/suspended/hibernate mode setINterval gets pauesd */
                    socket_write($client,encode("You are not active! your pc is at sleep"));
                    $_SESSION['prev_time'] = $jsTime;
                }
            }else { /** if we have not saved the previous time in session save it  */
                $_SESSION['prev_time'] = $jsTime;
            }

            print_r($_SESSION);

?>

E aqui está o código completo do php:

<?php
//Code by: Nabi KAZ <www.nabi.ir>
session_abort();
// set some variables
$host = "127.0.0.1";
$port = 34237;
date_default_timezone_set("UTC");


// don't timeout!
set_time_limit(0);

// create socket
$socket = socket_create(AF_INET, SOCK_STREAM, 0)or die("Could not create socket\n");

// bind socket to port
$result = socket_bind($socket, $host, $port)or die("Could not bind to socket\n");

// start listening for connections
$result = socket_listen($socket, 20)or die("Could not set up socket listener\n");

$flag_handshake = false;
$client = null;
do {
    if (!$client) {
        // accept incoming connections
        // client another socket to handle communication
        $client = socket_accept($socket)or die("Could not accept incoming connection\n");
    }

    $bytes =  @socket_recv($client, $data, 2048, 0);
    if ($flag_handshake == false) {
        if ((int)$bytes == 0)
            continue;
        //print("Handshaking headers from client: ".$data."\n");
        if (handshake($client, $data, $socket)) {
            $flag_handshake = true;
        }
    }
    elseif($flag_handshake == true) {

        /*
        **** Main section for detectin sleep or not **
        */
        if ($data != "") {
            $decoded_data = unmask($data /* $data is actual data received from javascript socket */); //grabbing data and unmasking it | unmasking is for javascript sockets don't mind this
            print("< ".$decoded_data."\n");
            $response = strrev($decoded_data);
            $jsTime = (int) $decoded_data; /* time sent by javascript in MILISECONDS IN UNIX FORMAT  */
            if (isset($_SESSION['prev_time'])) { /** check if we have stored previous time in the session */
               $prev_time = (int) $_SESSION['prev_time']; /** grabbing the previous time from session */
               $diff = $jsTime-$prev_time; /** getting the difference newly sent time and previous time by subtracting */
               print("$jsTime - $prev_time = $diff"); /** printing the difference */
               if($diff<3000){ /** checking if difference is less than 3 second if it is it means pc was not at sleep
                               *** you can manipulate and have for example 1 second = 1000ms */
                    socket_write($client,encode("You are active! your pc is awakend"));
                    $_SESSION['prev_time'] = $jsTime; /** saving newly sent time as previous time for future testing whcih will happen in two seconds in our case*/
                }else { /** if it is more than 3 seconds it means that javascript setInterval function was paused and resumed after 3 seconds 
                            ** So it means that it was at sleep because when your PC is at sleep/suspended/hibernate mode setINterval gets pauesd */
                    socket_write($client,encode("You are not active! your pc is at sleep"));
                    $_SESSION['prev_time'] = $jsTime;
                }
            }else { /** if we have not saved the previous time in session save it  */
                $_SESSION['prev_time'] = $jsTime;
            }

            print_r($_SESSION);

           /*
        **** end of Main section for detectin sleep or not **
        */ 


        }
    }
} while (true);

// close sockets
socket_close($client);
socket_close($socket);
$client = null;
$flag_handshake = false;

function handshake($client, $headers, $socket) {

    if (preg_match("/Sec-WebSocket-Version: (.*)\r\n/", $headers, $match))
        $version = $match[1];
    else {
        print("The client doesn't support WebSocket");
        return false;
    }

    if ($version == 13) {
        // Extract header variables
        if (preg_match("/GET (.*) HTTP/", $headers, $match))
            $root = $match[1];
        if (preg_match("/Host: (.*)\r\n/", $headers, $match))
            $host = $match[1];
        if (preg_match("/Origin: (.*)\r\n/", $headers, $match))
            $origin = $match[1];
        if (preg_match("/Sec-WebSocket-Key: (.*)\r\n/", $headers, $match))
            $key = $match[1];

        $acceptKey = $key.'258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
        $acceptKey = base64_encode(sha1($acceptKey, true));

        $upgrade = "HTTP/1.1 101 Switching Protocols\r\n".
            "Upgrade: websocket\r\n".
            "Connection: Upgrade\r\n".
            "Sec-WebSocket-Accept: $acceptKey".
            "\r\n\r\n";

        socket_write($client, $upgrade);
        return true;
    } else {
        print("WebSocket version 13 required (the client supports version {$version})");
        return false;
    }
}

function unmask($payload) {
    $length = ord($payload[1]) & 127;

    if ($length == 126) {
        $masks = substr($payload, 4, 4);
        $data = substr($payload, 8);
    }
    elseif($length == 127) {
        $masks = substr($payload, 10, 4);
        $data = substr($payload, 14);
    }
    else {
        $masks = substr($payload, 2, 4);
        $data = substr($payload, 6);
    }

    $text = '';
    for ($i = 0; $i < strlen($data); ++$i) {
        $text .= $data[$i] ^ $masks[$i % 4];
    }
    return $text;
}

function encode($text) {
    // 0x1 text frame (FIN + opcode)
    $b1 = 0x80 | (0x1 & 0x0f);
    $length = strlen($text);

    if ($length <= 125)
        $header = pack('CC', $b1, $length);
    elseif($length > 125 && $length < 65536)$header = pack('CCS', $b1, 126, $length);
    elseif($length >= 65536)
    $header = pack('CCN', $b1, 127, $length);

    return $header.$text;
}

NOTA LEIA: a $new_timevariável está $jsTimeno código

crie 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)

Dato DT
fonte
crie 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 ocorrerá 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)
Dato DT
0

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.

Harsh Srivastava
fonte
-1

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).

(function() {
    this.SleepTimer = function() {
        // console.log('sleep timer initiated');
        // Create global element references
        this.sleepTimer = null;
        this.maxTime = null;
        this.curDate = null;
        this.newDate = null;
        this.timer = null;
        this.timeInterval = 1000;

        this.sleepTimer = new CustomEvent("sleepTimer", {
		    "detail": {
		    	"maxTime":this.maxTime,
				"idelFor": this.newDate - this.curDate,
				"timer": this.timer
			}
		});

        // Define option defaults
        var defaults = {
            maxTime: 10000,
            timeInterval: 1000,
            autoStart: true,
            console: false,
            onStart: null,
            onIdel: null
        }
        // Create options by extending defaults with the passed in arugments
        if (arguments[0] && typeof arguments[0] === "object") {
            this.options = extendDefaults(defaults, arguments[0]);
        }
        if (this.options.timeInterval) {
            this.timeInterval = Math.max(1000, this.options.timeInterval);
            this.maxTime = Math.max(this.options.maxTime, 10000);
        } else {
        	this.options = defaults;
        }

        if(this.options.autoStart === true) this.start()
        // Utility method to extend defaults with user options
        
    }
    function extendDefaults(source, properties) {
        var property;
        for (property in properties) {
            if (properties.hasOwnProperty(property)) {
                source[property] = properties[property];
            }
        }
        return source;
    }
    SleepTimer.prototype.start = function(){
        var _ = this;
    	this.options.onStart()
        this.curDate = Date.now();

        this.timer = setInterval(function() {
            _.newDate = Date.now();
            var diff = _.newDate - _.curDate;

            // for debugging
            if(_.options.console && diff > _.timeInterval){
            	console.log('Your PC was idel for ' + diff / 1000 + 's of ' + _.maxTime /1000 + 's. TimeInterval is set to ' + _.timeInterval / 1000 + 's');
            }
            
            if (diff < _.maxTime) {
                _.curDate = _.newDate;
            } else {
            	_.options.onIdel();
                // alert('You have been idle for ' + diff / 1000 + 's');
                clearTimeout(_.timer);
            }
        }, this.timeInterval); // seconds
    }
}());

var sleepTimer = new SleepTimer({
	maxTime: 15000,
	console: true,
	onStart: function(){
		console.log('sleepTimer started.');
	},
	onIdel: function(){
		alert('Your session expired! Please login again.');
	}
});

Lasithds
fonte
Por favor, explique por que isso não funcionaria, se não acontecer no seu caso
Lasithds
-1

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 ACTIVEusuá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 do usernamevalor & 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 é ACTIVEe possui um Token válido, concedo a ele acesso ao recurso protegido E, o mais importante, faço outra inserção no Redis com o username& 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ão ACTIVEacessar 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 ACTIVEentrada.

Também é importante que você verifique se o que você usa para armazenar se um determinado usuário ACTIVEestá 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.

Snickers3192
fonte
Também sobrevivemos a um teste de caneta (; foi daí que surgiram nossos requisitos inicialmente, nosso aplicativo é um produto baseado em serviços financeiros e tivemos que implementá-lo como um requisito.)
Snickers3192