Вийдіть із веб-сайту, коли він перебуває в режимі сну


11

Це химерне. У нас є веб-сайт Laravel, і на вказаному сайті у нас є таймер на кожного користувача, де вони отримують 15 хвилин неактивності перед завантаженням.

Ми робимо це через таймер, який сидить на сторінці в реагуючому компоненті, він працює так, як ми цього хочемо, але тепер у нас є нова проблема: Якщо користувач увійшов у систему та закрив кришку свого ноутбука, веб-сайт повинен завантажувати їх. . Банки роблять це, школи та університети роблять це, урядові сайти також роблять це. Так можливо, просто не знаю як.

Ми використовуємо веб-розетки, використовуючи бібліотеку laravel-websockets та Echo. Що я хотів би бачити:

  • Як тільки ви закриєте ноутбук, завантажте вас на екран входу. Тож наступного разу, коли ви відкриєте ноутбук і ввійдете в систему, і побачите браузер, який ви перебуваєте на екрані входу. Це не повинно відбуватися так швидко, але нам потрібен спосіб надіслати щось на передовій частині, в основному кажучи їм оновити сторінку, як тільки сеанс буде вбито, ми встановлюємо тривалість сеансу на рівні 15 хвилин.

Деякі люди пропонували в інших подібних питаннях:

  • створити користувацький обробник веб-сокетів
  • Для порівняння файлу cookie сеансу (у веб-переглядачі) з файлом cookie користувача на зворотному кінці.
  • Щоб таймер працював на передній частині (ми це робимо, він просто зупиняється, коли ви закриваєте кришку ноутбука)

Здається, найпопулярнішим є використання веб-сокетів, прослуховування для того, щоб користувач відключився, а потім завантажив їх, що нормально, і все, але як тоді ви надсилаєте запит до призупиненого браузера, щоб потім їх завантажувати?

Я знайшов requestIdleCallback () Але знову ж таки, я не думаю, що це те, що я хочу, якщо в мене вже є таймер серцебиття. Він також працює не у всіх браузерах.

Я дуже розгублений, як це досягти, я можу навести приклад:

Увійдіть до свого банку, укладіть комп'ютер у режим сну, зачекайте 15-20 хвилин, розбудіть комп’ютер, увійдіть у систему та побачте, що ваш банк зараз має вас на екрані входу. Це я хочу . Але я не знаю, як це досягти.

Ви не можете надсилати події до "сплячого" браузера із заднього кінця, і хоча так, це повинно бути рішенням із зворотнього кінця, як потім оновити передню частину, щоб вони знаходилися на екрані виходу, коли вони знову пробуджують ноутбук чи комп’ютер?


7
Просто встановіть дату закінчення терміну дії cookie сеансу на "зараз + 15 хвилин" ... хоча ви все ще можете побачити останній екран, ви не можете діяти на цьому сеансі, якщо термін cookie закінчився. І "екран завантаження для входу" - я майже впевнений, що є налаштування ОС, яке автоматично вийде з комп'ютера через певний час бездіяльності ..
Ларс Стегеліц

1
Ви можете запускати таймер Javascript кожного разу, коли сайт завантажується, з тим самим тайм-аутом, що і ваш cookie сеансу ... якщо таймер "дзвонить", користувач був так неактивний. Потім ви вийдете з нього (дзвінок AJAX) і перенаправите веб-переглядач на екран входу або на екран "вибачте, ми вийшли з системи через неактивність";
Ларс Стегеліц

1
Гаразд, я це зробив, зараз додаю відповідь.
Dato DT

1
так це було корисно?
Dato DT

1
@DatoDT Ні, один я використовую laravel, два я вже використовую розетки laravel, як це виражено у моєму вступному дописі, два ваш код дуже безладний, не OOP, не перевірений, і я б ніколи його не використовував. Я по суті шукаю рішення laravel.
TheWebs

Відповіді:


0

ОНОВЛЕННЯ

Щодо запиту WebSocket, я припускаю, що ви використовуєте Laravel WebSockets з pusher. Pusher.io не підтримує тайм-аут , ви можете прочитати цю статтю підтримки "Ви плануєте додати функцію очікування з'єднання в бібліотеку клієнтів push-js клієнта?" . Ви можете перевірити це, якщо ввімкнути режим налагодження Laravel ( APP_DEBUG=true всередині .env ) та виправити laravel-websocketsз терміналу ( php artisan websockets:serve), щоб ви могли бачити події журналу виводу. Якщо ви спробуєте закрити кришку ноутбука або перевести комп'ютер у режим сну ( сон ), ви не побачите жодних повідомлень щодо цієї події. Ви не можете зробити це з pusherпротоколом. Відбувається подія присутностіmember_removed , але це спрацьовує лише тоді, коли ви закриєте вкладку або виходите з системи. Звичайно, ви можете викликати власну подію клієнта на канал присутності, але для цього вам також потрібно встановити таймер на стороні клієнта, і вам доведеться створити постачальника послуг для laravel-websocketsсервера, як ця проблема github. "Існує спосіб реалізувати веб-гачки? " .

Деякі люди пропонували в інших подібних питаннях:

...

  • Щоб таймер працював на передній частині ( ми це робимо, він просто зупиняється, коли ви закриваєте кришку ноутбука )

Це відбувається тому, що таймери клієнтів зупиняють виконання в сплячому режимі, таким чином вони продовжуються там, де були раніше. Але якщо ви використовуєте змінну дати, щоб заощадити час , ця змінна не буде оновлюватися, коли комп'ютер переходить у сплячий режим, тож ви дізнаєтесь, коли вона вийде зі сну, перевіривши ту змінну дати, яка порівняно з поточним часом матиме значне значення різниця і буде більше інтервалу таймера.

Реалізація логіки часу в клієнті

Ви також можете побачити цю реалізацію для цього пов'язаного запитання : Чи можуть будь-які браузери настільних комп'ютерів виявити, коли комп'ютер відновлюється зі сну?

Ви можете налаштувати таймер у клієнта, щоб він працював щохвилини. Ми не покладаємось на інтервал таймера , але замість цього він перевірить змінну дати зовнішньої області застосування, якщо часовий проміжок часу з останнього таймера більший за 15хвилину; якщо це так, то це означає, що браузер / JS з якоїсь причини зупинив виконання , можливо, сплячий пристрій ( сон ), а потім ви перенаправляєте користувача до маршруту виходу.

Приклад клієнтського коду 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);

Ви можете перевірити цей простий приклад, але тут використовується 1другий таймер із 15секундами виходу . Найкраще протестувати його на ноутбуці, закривши кришку, а потім знову відкрити її через 15 секунд через дві хвилини, тому що якщо у вас працює багато програм, комп'ютеру потрібен певний час, щоб зберегти стан пам'яті, щоб завершити режим сплячки і зупинити виконання.

Приклад веб-працівників

Ви навіть можете використовувати API Web Workers для налаштування веб-працівника, щоб бути набагато безпечнішим:

Код JS сторінки:

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.jsКод веб-працівника :

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

Ви також можете перевірити приклад веб-робітника з тим же 15таймером секунд тут .


Це видасть вас через 15 секунд незалежно від того, що тхо, не впевнений, що це те, що він шукає.
Іслам Елшобокші

@IslamElshobokshy ви праві, дякую за улов. Я забув оновити clientSessionзмінну. Ви можете перевірити мою відповідь ще раз, я навіть додав приклад Web Worker.
Крістос Літрас

Оновіть приклади зразків, які все ще не працюють. Перший виходить із системи через 15 секунд, незважаючи ні на що. Другий ніколи вас не виписує.
Іслам Елшобокші

@IslamElshobokshy Я, звичайно, оновив його. Раніше у нього була проблема, а зараз вона працює, як очікувалося. Будь ласка, оновіть сторінку, якщо ви цього не зробили або, можливо, додати параметр, як ?v=1до кінця.
Крістос Літрас

1
Велике рішення + 3
AmerllicA

0

По-перше, давайте розкриємо, чому банківські веб-сайти виходять через 15 хвилин без активності. Це вимога PCI для безпеки.

Вимога PCI-DSS 8.1.8 :

8.1.8 Якщо сеанс простояв більше 15 хвилин, вимагайте від користувача повторної аутентифікації, щоб повторно активувати термінал або сеанс.

Для досягнення цього рішення насправді набагато примітивніше, ніж ви собі уявляєте . Він не вимагає використання веб-розеток і нічого не знаючи про стан клієнтської машини (сон або неспання чи інше). Все, що потрібно, - це знати час між поточним запитом, що використовує цей сеанс, і останнім запитом, використовуючи один і той же сеанс, і гарантувати, що вони не перевищують 15 хвилин. Якщо вони є, користувач підлягає повторній автентифікації. Якщо їх немає, можете продовжити запит.

Повідомлення "Вичерпано сеанс"

Ви, мабуть, тоді задаєтесь питанням (якщо це так просто), як з’являється повідомлення про час сеансу, коли ви кладете комп'ютер у режим сну і будите його назад. Ця частина оманливо проста.

Коли комп'ютер перебуває уві сні, браузер фактично відключає всі TCP / IP-з'єднання, що, в свою чергу, вимикає цикл подій у механізмі javascript. Тож таймери не працюють. Але коли браузер знову прокидається, він намагається оновити деякі речі, включаючи саму сторінку. Отже, коли сторінку оновлюють, запит повертається на сервер, що викликає сервер, щоб вимагати від користувача повторної автентифікації.

Однак це не враховує модальний режим JavaScript (якщо це саме ви маєте на увазі), який роблять деякі банківські веб-сайти. Крім того, не всі веб-переглядачі ретельно оновлюють сторінку у всіх сценаріях. Тож можна застосувати інший підхід. Замість того, щоб у веб-переглядачі з’явився таймер, який закінчується через 15 хвилин, ви можете просто зберігати час завантаження сторінки в JavaScript як часову позначку і мати інтервал часу в 1 секунду, який порівнює цю часову позначку з поточною міткою часу на комп’ютері. Якщо на відстані більше 15 хвилин, сеанс слід припинити.

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


};

Навіть якщо комп'ютер переходить у режим сну і таймер зупиняється, сеанс з часом вимкнеться на стороні сервера (детальніше див. Розділ нижче ), і коли комп'ютер знову прокинеться, таймер з інтервалом у 1 секунду з часом запуститься знову, викликаючи повідомлення (як би користувач вимкнув час, поки комп'ютер спить). Час, втрачений між часом, коли комп'ютер лягає спати, і часом, коли комп'ютер прокидається, не має значення, оскільки часова мітка залишиться в пам'яті. Відключення між клієнтом та сервером неважливо, оскільки їм не потрібно передавати цю інформацію для того, щоб сеанс був належним чином припинений на стороні сервера. Сервер може робити власне збирання сміття та припиняти сеанс без зв'язку з клієнтом (тобто асинхронно ).

Вірите чи ні, Банки не дбають про діяльність всередині клієнта. Вони дбають лише про запит на сервер. Тож якщо вам цікаво, як вони підтримують сеанс живим довше 15 хвилин, коли користувач так довго знаходиться на одній сторінці, вони просто надсилають запит AJAX у фоновому режимі, щоб оновити сеанс після запиту користувача, чи все-таки вони хочу продовжувати.

Це можна зробити у тому самому onloadзворотному зворотному звороті події, який ми використовували раніше так:

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


};

Обробка завершення сеансу на стороні сервера

Для обробки припинення сеансу на стороні сервера існує кілька підходів. Залежно від того, який ви використовуєте, вам знадобляться різні тактики. Перший - це використання обробника сеансу PHP за замовчуванням та встановлення session.max_lifetimeтерміну дії, який закінчується через 15 хвилин (це видаляє дані сеансу повністю на стороні сервера, таким чином виключаючи файли cookie клієнта).

Якщо ви дозволите механізму обробника сеансу за замовчуванням це зробити, ви можете зіткнутися з проблемами залежно від того, який обробник використовується (файли, запам’ятовування, перероблення, користувацькі тощо).

З файлами (обробник за замовчуванням) збирання сміття відбувається одним із двох способів:

  • Більшість систем на базі Debian роблять власний GC за допомогою роботи cron (що чудово підходить для вашого сценарію)
  • Інші дистрибутиви дозволяють механізму GC за замовчуванням PHP обробляти це, що базується на ймовірнісному результаті кожного вхідного запиту до PHP, який перевіряє файли mtime на файли сеансу та видаляє ті, що минули їх session.max_lifetime. Проблема такого підходу полягає в тому, що на сайтах з низьким трафіком сеанс потенційно може сидіти там на сервері тривалий час, поки не надійде достатньо запитів (залежно від session.gc_probabilityоцінки), щоб викликати GC для очищення файлів сеансу.

З оброблюваними на основі шаблонів і переробленими у вас проблемами не виникає Вони справляться з очищенням пам'яті автоматично. Сесії все ще можуть залишатися у фізичній пам’яті протягом певного часу, але демон не зможе отримати до них доступ. Якщо ви стурбовані цим бітом для безпеки, ви можете зашифрувати свої сеанси в спокої або знайти сховище ключа / значення, яке має більш жорсткий механізм очищення пам'яті GC.

За допомогою спеціального обробника сеансу вам доведеться створити власний механізм GC. Завдяки цьому SessionHandlerInterfaceви впровадите gcметод, який передає вам максимальний інтервал життя сеансу, і ви будете нести відповідальність за перевірку того, чи пройшов сеанс його термін служби на основі цього інтервалу, і зробіть звідти ваш сміття.

Ви також можете встановити окрему кінцеву точку, яка перевіряє TTL сеансу (за допомогою асинхронного запиту AJAX на стороні клієнта) та відправляє відповідь, якщо сеанс минув (змушує javascript повторно аутентифікувати користувача).


0

Тож Idea стоїть за setInterval та Sockets, setInterval підтримується у більшості браузерів, а JavaScript WbsocketApi підтримується майже у кожному браузері.

Короткий огляд: setInterval () - ця функціональна поведінка слідує, коли ваш комп'ютер знаходиться у режимі сну / призупинення / сну, він призупиняється, а коли ви перебуваєте в режимі пробудження, він відновлюється.

Наступний код робить наступне: спочатку (можливо, в той же час, але) він запускає php server_socket прослуховування з'єднань,

ніж javascript websocket api надсилає поточну мітку часу в часовій мітці Unix мілісекунди кожні 2 секунди, ви можете мати 1 секунду, це залежить від вас.

після цього розетка сервера PHP отримує цей час і перевіряє, чи є у нього щось подібне до попереднього разу для порівняння, коли код вперше інстанційований, php не має нічого подібного до попереднього разу, щоб порівняти його з часом, надісланим з веб-сокета Javascript, так що php не робить нічого, крім цього економить цей час на сеансі під назвою 'prev_time' і чекає, коли дані інший час будуть отримані з сокета javascript, тому тут починається другий цикл. коли php сервер socket нові дані часу від javascript WebsocketApi він перевіряє, що він має щось подібне до попереднього разу порівняти з цим нещодавно отриманими даними часу, це означає, що php перевіряє, чи існує сесія під назвою 'prev_time', як ми в другому циклі php виявляє, що воно існує, схоплює його цінність і робить наступне$diff = $new_time - $prev_time, $ diff становитиме 2 секунди або 2000 мілісекунд, тому що пам’ятайте наш цикл setInterval відбувається кожні 2 секунди, а формат часу, який ми надсилаємо, - у мілісекундах,

ніж php перевіряє, if($diff<3000)якщо різниця менше 3000, якщо це знає, що користувач активний, знову ви можете маніпулювати цими секундами, як хочете, я вибираю 3000, оскільки можлива затримка в мережі, що майже неможливо, але ви знаєте, я завжди обережний, коли що стосується мереж, тож продовжимо, коли php визначає, що користувач активний, php просто скидає сеанс 'prev_time', значення $new_timeякого було нещодавно отримане, і лише з метою тестування він надсилає повідомлення назад у гніздо javascript,

але якщо $diffбільше 3000, це означає, що щось призупинило наш набір Інтервалу, і є лише спосіб, що це може статися, і я думаю, ви вже знаєте, про що я говорю, тому за elseлогікою ( if($diff<3000)) ви можете вийти з користувача, знищивши конкретний сеанс, і якщо ви Якщо ви хочете перенаправити, ви можете надіслати текст у sova javacript і створити логіку, яка буде виконуватися window.location = "/login"залежно від тексту, ось це код:

Спочатку це файл index.html просто для завантаження javascript:

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

тоді це javascript, це не дуже гарно закодовано, але ви можете зрозуміти, ЧИТАЙТЕ КОМЕНТАРІ, ЩО ВАЖЛИВО:

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);
};

тепер ось частина php-коду, не хвилюйтесь, там є і повний код, але ця частина насправді є тим, що виконує вищезазначені завдання, ви також будете відповідати і іншим функціям, але вони призначені для розшифровки та роботи з javascript-сокетами, так що це фактично правильно тут ЧИТАЙТЕ КОМЕНТАРІ, ЩО ВАЖЛИВО:

<?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);

?>

І ось повний код 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;
}

ПРИМІТКА ПРОЧИТАЙТЕ: $new_timeзмінна $jsTimeв коді

створіть папку та просто скопіюйте та вставте це у файли, запускайте php socket за допомогою команди: php -f server_socket.php перейдіть до localhost і протестуйте його на відкритій консолі, щоб побачити повідомлення, на яких буде сказано "ви активні" або "ви не активні" (коли ви прийшли зі сну); ваш executin відбудеться тоді, коли користувач прийде зі сну, а не тоді, коли він перебуває уві сні, тому що в цей момент усе кешується у файлі сторінки (windows) або в swap (linux)


створіть папку та просто скопіюйте та вставте це у файли, запускайте php socket за допомогою команди: php -f server_socket.php перейдіть до localhost і протестуйте його на відкритій консолі, щоб побачити повідомлення, на яких буде сказано "ви активні" або "ви не активні" (коли ви прийшли зі сну); ваш екзекутін станеться, коли користувач прийде зі сну, а не тоді, коли він спить, тому що в цей момент усе кешовано у файлі сторінки (windows) або в swap (linux)
Dato DT

0

Я думаю, що я маю ідею, ви багато обговорювали про те, як працює система входу / виходу з банку.

Випадок-1: Доступ веб-сторінки до користувача протягом необмеженого часу, якщо користувач активний

Щоразу, коли користувач увійшов у систему, запустіть таймер на своєму бекенді (встановіть обмеження часу, що вам потрібно), скажімо, 15 хвилин. Тепер що це означає ?? Це означає, що якщо користувач не здійснює жодної активності на веб-сторінці, ми виходимо з нього.

Тепер, спереду, ви можете надіслати активність користувача на ваш бекенд (можна надіслати за допомогою сокета або тривалого опитування), що в основному скине таймер, і користувач може активно використовувати веб-сторінку в будь-який час.

Якщо користувач перейшов у режим сну, таймер не скинеться, і ви можете визначити недійсним сеанс після закінчення таймера.

Якщо ви хочете визначити недійсним сеанс користувача, як тільки він переводить ПК у режим сну, ви можете встановити ліміт часу перевірки сеансу. Наприклад, під час входу користувача ми створимо сеанс, який буде дійсним лише 10 секунд, і як тільки ми отримаємо запит на активність користувача, ми можемо скинути таймер та надати новий ключ сеансу.

Я сподіваюся, що це вам допоможе. Повідомте мене, якщо у вас виникли запитання.


-1

Я написав сценарій, щоб виявити, чи машина заснула. Ідея полягає в тому, що коли машина перебуває в режимі сну, всі сценарії припиняться. Тому якщо ми відстежуємо поточний час протягом інтервалу time. Кожен раз, коли timeInterval запускає поточний час мінус (-), новий час повинен бути достатньо близьким до інтервалу time. Для цього, якщо ми хочемо перевірити, чи таймер не працював протягом X часу, ми можемо перевірити, чи різниця у часі більше X.

Приклад удару перевіряє, чи комп'ютер перебуває уві сні більше 15 секунд. Зауважте, що коли ви кладете комп'ютер спати, це потребує ще додаткових 15-ти, щоб уявити всі процесори. (При тестуванні на моєму ПК).

(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

-1

Я реалізував таку саму вимогу, використовуючи AWS Cognito, з Lambda Autizers & Redis, я не можу поділитися кодом на цьому етапі, але я можу розповісти вам усе, як він реалізований з цими компонентами, ті ж самі поняття можна використовувати з іншими не AWS компоненти.

По-перше, реалізуючи вихід бездіяльності, вам доведеться це зробити на сервері, так як якщо хтось просто вимикає свій комп’ютер, веб-сайт на передньому рівні не виходитиме з них. Я використовував концепцію ACTIVEкористувачів. Коли користувачі успішно підтверджують автентифікацію, я зберігаю з TTL протягом 15 хвилин у Redis запис із ключем їх username& значення ACTIVE(це може бути ім'я користувача + sessionid, якщо ви хочете дозволити кілька сеансів для певного користувача одночасно).

У моїх користувальницьких авторизаторах, коли користувачем є ACTIVE& у них є дійсний токен, я надаю їм доступ до захищеного ресурсу І найголовніше - я роблю ще один додаток у Redis разом із username& ACTIVE.

Щоразу, коли користувач виходить із системи, я виходжу з них у моє рішення для управління ідентифікацією (Cognito), і я позначаю їх як INACTIVE. Зауважте, що якщо користувач не натисне API протягом 15 хвилин, він більше не матиме запис ACTIVEпроти свого імені користувача і більше не зможе отримати доступ до API і доведеться знову входити, для чого він буде переспрямований.

З цим підходом слід враховувати багато речей, оскільки одна справа часто авторизує кешування результатів протягом певного часу, і якщо ви скажете, що ви кешуєте результат за 5 хвилин як приклад, то ваш користувач може вийти з системи за 10 хвилин як ваш користувач може потрапити в кеш-пам'ять замість Авторизатора, що не оновить ACTIVEзапис.

Також важливо переконатися в тому, що ви використовуєте для зберігання, якщо даний користувач ACTIVEвисокодоступний і швидко відновиться у разі відмови.

Таким чином, підхід до використання сховища кеша схожий на те, як недійсне встановлення маркерів перетворюється на протоколи авторизації без громадянства, такі як OAuth2.

Ми використовуємо цей підхід вже декілька місяців, він, здається, спрацьовує добре для нас, це може бути трохи прикрою вимогою, щоб я впорався, я очікував у світі AWS, що буде готовий використати рішення про коробку для цього, але не було про кого говорити.


Ми також пережили тест на ручку (; звідси випливала наша вимога, наша заявка є продуктом, що базується на фінансових послугах, і нам довелося реалізувати це як вимогу.
Snickers3192,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.