บันทึกผู้ใช้ออกจากเว็บไซต์เมื่อพวกเขาทำให้คอมพิวเตอร์เข้าสู่โหมดสลีป


11

นี่คือหนึ่งที่แปลกประหลาด เรามีเว็บไซต์ Laravel และในเว็บไซต์ดังกล่าวเรามีตัวจับเวลาต่อผู้ใช้ซึ่งพวกเขาจะไม่ได้ใช้งาน 15 นาทีก่อนที่จะถูกบูท

เราทำสิ่งนี้ผ่านตัวจับเวลาที่ตั้งอยู่บนหน้าในส่วนประกอบแบบตอบสนองมันทำงานได้ตามที่เราต้องการ แต่ตอนนี้เรามีปัญหาใหม่: หากผู้ใช้เข้าสู่ระบบและปิดฝาแล็ปท็อปเว็บไซต์ควรบูตพวกเขา . ธนาคารทำเช่นนี้โรงเรียนและมหาวิทยาลัยทำเช่นนี้ไซต์รัฐบาลก็ทำเช่นนี้ ดังนั้นจึงเป็นไปได้ แต่ก็ไม่แน่ใจเหมือนกัน

เราใช้ซ็อกเก็ตเว็บโดยใช้ไลบรารี laravel-websocketsและ Echo สิ่งที่ฉันต้องการเห็นเกิดขึ้นคือ:

  • เมื่อคุณปิดแล็ปท็อปบูตคุณไปที่หน้าจอเข้าสู่ระบบ ครั้งต่อไปที่คุณเปิดแล็ปท็อปและล็อกอินและดูเบราว์เซอร์ที่คุณอยู่บนหน้าจอเข้าสู่ระบบ ไม่จำเป็นต้องเกิดขึ้นอย่างรวดเร็ว แต่เราต้องการวิธีส่งบางอย่างไปยังส่วนหน้าโดยทั่วไปบอกให้รีเฟรชหน้าเว็บเมื่อเซสชั่นถูกฆ่าตายเราจะกำหนดอายุการใช้งานเซสชันเป็น laravel 15 นาที

บางคนแนะนำในคำถามที่คล้ายกันอื่น ๆ :

ดูเหมือนจะใช้ซ็อกเก็ตเว็บที่ได้รับความนิยมมากที่สุดฟังเพื่อให้ผู้ใช้ปลดการเชื่อมต่อจากนั้นทำการบู๊ตซึ่งเป็นสิ่งที่ดีและทั้งหมด แต่แล้วคุณจะส่งคำขอไปยังเบราว์เซอร์ที่ถูกระงับแล้ว

ฉันได้พบrequestIdleCallback ()แต่อีกครั้งฉันไม่คิดว่านี่คือสิ่งที่ฉันต้องการถ้าฉันมีตัวจับเวลา heartbeat บนเว็บไซต์ มันไม่สามารถใช้ได้กับทุกเบราว์เซอร์

ฉันหลงทางมากเกี่ยวกับวิธีการทำสิ่งนี้ให้สำเร็จตัวอย่างที่ฉันให้คือ:

เข้าสู่ระบบธนาคารของคุณ, ทำให้คอมพิวเตอร์เข้าสู่โหมดสลี, รอ 15-20 นาที, ปลุกคอมพิวเตอร์, เข้าสู่ระบบและดูธนาคารของคุณตอนนี้มีหน้าจอเข้าสู่ระบบ นั่นคือสิ่งที่ฉันต้องการ แต่ฉันไม่รู้วิธีที่จะทำให้สำเร็จ

คุณไม่สามารถส่งกิจกรรมไปที่เบราว์เซอร์ "sleep" จาก back end และในขณะที่ใช่สิ่งนี้จะต้องเป็น back end solution คุณจะอัพเดท front end ได้อย่างไรเพื่อให้พวกเขาอยู่ในหน้าจอล็อกเอาต์เมื่อเปิดแล็ปท็อปอีกครั้ง หรือคอมพิวเตอร์


7
เพียงตั้งค่าวันหมดอายุของเซสชัน Cooke เป็น "ตอนนี้ + 15 นาที" ... แม้ว่าคุณจะยังคงเห็นหน้าจอสุดท้ายคุณจะไม่สามารถดำเนินการในเซสชันนั้นได้หากคุกกี้หมดอายุ และหน้าจอ "boot to login" - ฉันค่อนข้างแน่ใจว่ามีการตั้งค่าระบบปฏิบัติการที่จะออกจากระบบคอมพิวเตอร์โดยอัตโนมัติหลังจากไม่มีการใช้งานในช่วงเวลาหนึ่ง ..
Lars Stegelitz

1
คุณสามารถเริ่มจับเวลา Javascript เมื่อใดก็ตามที่มีการโหลดเว็บไซต์ด้วยการหมดเวลาเช่นเดียวกับคุกกี้เซสชั่นของคุณ ... หากตัวจับเวลา "แหวน" ผู้ใช้ไม่ได้ใช้งานมานาน จากนั้นคุณจะออกจากระบบ (โทร AJAX) และเปลี่ยนเส้นทางเบราว์เซอร์ไปที่หน้าจอเข้าสู่ระบบหรือ "ขออภัยเราได้นำคุณออกจากหน้าจอเนื่องจากไม่มีการใช้งาน")
Lars Stegelitz

1
ตกลงฉันทำเสร็จแล้วฉันกำลังเพิ่มคำตอบตอนนี้
Dato DT

1
เช่นนี้มีประโยชน์หรือไม่
Dato DT

1
@DatoDT ไม่ฉันใช้ laravel สองฉันใช้ซ็อกเก็ต laravel ดังที่แสดงในโพสต์เปิดของฉันสองรหัสของคุณยุ่งมากไม่ใช่ OOP ไม่ได้ทดสอบและฉันจะไม่ใช้มัน ฉันกำลังมองหาวิธีการแก้ปัญหา laravel เป็นหลัก
TheWebs

คำตอบ:


0

UPDATE

เกี่ยวกับคำขอ WebSocket ผมถือว่าคุณกำลังใช้WebSockets Laravelpusherกับ Pusher.io ไม่สนับสนุนการหมดเวลาคุณสามารถอ่านบทความสนับสนุนนี้"คุณวางแผนที่จะเพิ่มฟีเจอร์ไทม์เอาต์การเชื่อมต่อไปยังไลบรารีไคลเอ็นต์แชนเนล pusher-js หรือไม่" . คุณสามารถทดสอบได้หากคุณเปิดใช้งานโหมดดีบัก Laravel ( APP_DEBUG=true ด้านใน .env ) และเริ่มlaravel-websocketsจากเทอร์มินัล ( php artisan websockets:serve) เพื่อให้คุณสามารถดูเหตุการณ์บันทึกผลลัพธ์ ถ้าคุณพยายามที่จะปิดฝาแล็ปท็อปหรือคอมพิวเตอร์ชุดไปยังโหมดไฮเบอร์เนต ( นอนหลับ ) คุณจะไม่เห็นข้อความใด ๆ ที่เกี่ยวกับเหตุการณ์นี้ คุณไม่สามารถทำได้ด้วยpusherโปรโตคอล มีเหตุการณ์การแสดงตนmember_removed แต่สิ่งนั้นจะทริกเกอร์เมื่อคุณปิดแท็บหรือออกจากระบบ แน่นอนว่าคุณสามารถเรียกใช้เหตุการณ์ที่กำหนดเองของลูกค้าไปยังช่องแสดงตนได้ แต่คุณต้องตั้งค่าตัวจับเวลาไปที่ฝั่งไคลเอ็นต์และคุณจะต้องสร้างผู้ให้บริการสำหรับlaravel-websocketsเซิร์ฟเวอร์เช่นปัญหา GitHub นี้"มีวิธีในการ ใช้ webhooks หรือไม่ " .

บางคนแนะนำในคำถามที่คล้ายกันอื่น ๆ :

...

  • หากต้องการให้ตัวจับเวลาทำงานที่ส่วนหน้า ( เราจะหยุดเมื่อคุณปิดฝาแล็ปท็อป )

สิ่งนี้เกิดขึ้นเนื่องจากตัวจับเวลาของไคลเอ็นต์หยุดการประมวลผลการจำศีลดังนั้นพวกเขาจึงดำเนินการต่อจากที่ที่เคยเป็น แต่ถ้าคุณใช้ตัวแปรวันที่เพื่อประหยัดเวลาตัวแปรนั้นจะไม่ได้รับการปรับปรุงเมื่อคอมพิวเตอร์เข้าสู่โหมดไฮเบอร์เนตดังนั้นคุณจะรู้ว่าเมื่อไรที่มันจะออกจากโหมดสลีปโดยตรวจสอบตัวแปรวันที่ซึ่งเมื่อเปรียบเทียบกับเวลาปัจจุบัน ความแตกต่างและจะมากกว่าช่วงเวลาจับเวลา

การใช้ตรรกะเวลาในไคลเอนต์

นอกจากนี้คุณยังสามารถเห็นการใช้งาน Q / A ที่เกี่ยวข้องนี้ : เบราว์เซอร์เดสก์ท็อปใด ๆ สามารถตรวจพบได้เมื่อคอมพิวเตอร์ออกจากโหมดสลีป

คุณสามารถตั้งค่าตัวจับเวลาในไคลเอนต์เพื่อเรียกใช้ในแต่ละนาที เราจะไม่พึ่งพาช่วงเวลาตัวจับเวลาแต่ตัวจับเวลานั้นจะตรวจสอบตัวแปรวันที่ของขอบเขตด้านนอกหากช่วงเวลานับตั้งแต่ตัวจับเวลาล่าสุดมากกว่า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 วินาทีต่อนาทีเพราะถ้าคุณมีหลายโปรแกรมที่ทำงานอยู่คอมพิวเตอร์จะใช้เวลาสักครู่ในการบันทึกสถานะหน่วยความจำเพื่อให้โหมดไฮเบอร์เนตเสร็จสมบูรณ์

ตัวอย่างคนงานเว็บ

คุณสามารถใช้Web Workers APIเพื่อตั้งค่าผู้ปฏิบัติงานเว็บให้ปลอดภัยยิ่งขึ้น:

รหัส 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);

นอกจากนี้คุณยังสามารถตรวจสอบตัวอย่าง Web Worker เดียวกับ15วินาทีจับเวลาที่นี่


การดำเนินการนี้จะนำคุณออกจากระบบหลังจาก 15 วินาทีไม่ว่าอะไรจะเกิดขึ้นก็ตามไม่แน่ใจว่านี่คือสิ่งที่เขาต้องการ
ศาสนาอิสลาม Elshobokshy

@ IslamElshobokshy คุณพูดถูกต้องขอบคุณที่จับ ฉันลืมปรับปรุงclientSessionตัวแปร คุณสามารถตรวจสอบคำตอบของฉันอีกครั้งฉันได้เพิ่มตัวอย่าง Web Worker
Christos Lytras

โปรดอัปเดตตัวอย่างตัวอย่างที่ยังใช้งานไม่ได้ ครั้งแรกที่คุณออกจากระบบหลังจาก 15 วินาทีไม่ว่าอะไรจะเกิดขึ้น ที่สองไม่เคยออกจากคุณ
ศาสนาอิสลาม Elshobokshy

@IslamElshobokshy ฉันได้อัปเดตแน่นอน ก่อนที่จะมีปัญหาและตอนนี้ก็ทำงานได้ตามที่คาดไว้ โปรดแสดงหน้าอีกครั้งหากคุณยังไม่ได้เพิ่มหรือเพิ่มพารามิเตอร์?v=1ในตอนท้าย
Christos Lytras

1
โซลูชันที่ยอดเยี่ยม + 3
AmerllicA

0

ก่อนอื่นมาขยายเหตุผลที่เว็บไซต์ธนาคารออกจากคุณหลังจาก 15 นาทีโดยไม่มีกิจกรรม มันเป็นข้อกำหนดของ PCI เพื่อความปลอดภัย

ข้อกำหนด PCI-DSS 8.1.8 :

8.1.8 หากเซสชันไม่ได้ใช้งานนานกว่า 15 นาทีกำหนดให้ผู้ใช้ต้องตรวจสอบสิทธิ์อีกครั้งเพื่อเปิดใช้งานเทอร์มินัลหรือเซสชันอีกครั้ง

เพื่อที่จะบรรลุเป้าหมายนี้การแก้ปัญหานั้นมีความดั้งเดิมมากกว่าที่คุณคิดเอาไว้ ไม่ต้องการการใช้งาน websockets และไม่ต้องรู้อะไรเกี่ยวกับสถานะของเครื่องของลูกค้า (พักหรือตื่นหรืออย่างอื่น) สิ่งที่จำเป็นต้องมีคือการทราบเวลาระหว่างคำขอปัจจุบันที่ใช้เซสชันนั้นและคำขอล่าสุดโดยใช้เซสชันเดียวกันและทำให้มั่นใจว่าจะไม่เกิน 15 นาที หากพวกเขาเป็นผู้ใช้จะต้องมีการรับรองความถูกต้องอีกครั้ง หากไม่ใช่คุณสามารถดำเนินการตามคำขอได้

ข้อความ "เซสชันหมดเวลา"

คุณอาจสงสัยว่า (ถ้าง่ายขนาดนั้น) เซสชันข้อความหมดเวลาจะปรากฏขึ้นอย่างไรเมื่อคุณทำให้คอมพิวเตอร์เข้าสู่โหมดสลีปและปลุกอีกครั้ง ส่วนนี้เป็นเรื่องง่ายที่หลอกลวง

เมื่อคอมพิวเตอร์เข้าสู่โหมดสลีปเบราว์เซอร์จะยกเลิกการเชื่อมต่อการเชื่อมต่อ TCP / IP ทั้งหมดซึ่งจะปิดลูปเหตุการณ์ในเอ็นจิ้น javascript ดังนั้นตัวจับเวลาไม่ทำงาน แต่เมื่อเบราว์เซอร์กลับมาทำงานอีกครั้งจะพยายามรีเฟรชบางสิ่งรวมถึงหน้าเว็บด้วย ดังนั้นเมื่อมีการรีเฟรชหน้าคำขอจะกลับไปที่เซิร์ฟเวอร์ที่เรียกใช้เซิร์ฟเวอร์เพื่อให้ผู้ใช้รับรองความถูกต้องอีกครั้ง

อย่างไรก็ตามสิ่งนี้จะไม่ส่งผลต่อข้อความข้อความจาวาสคริปต์ (หากเป็นสิ่งที่คุณกำลังอ้างถึง) ซึ่งเว็บไซต์ธนาคารบางแห่งทำ นอกจากนี้เบราว์เซอร์บางตัวยังไม่รีเฟรชอย่างหนักในหน้าเว็บในทุกสถานการณ์ ดังนั้นวิธีการอื่นสามารถทำได้ แทนที่จะมีตัวจับเวลาในเบราว์เซอร์ที่หมดเวลาหลังจาก 15 นาทีคุณสามารถจัดเก็บเวลาในการโหลดหน้าเว็บในรูปแบบจาวาสคริปต์เป็นเวลาประทับและมีช่วงเวลา 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 นาที (สิ่งนี้จะลบข้อมูลเซสชันทั้งหมดในฝั่งเซิร์ฟเวอร์จึงทำให้คุกกี้ของลูกค้าใช้ไม่ได้)

หากคุณให้กลไกตัวจัดการเซสชันเริ่มต้นทำคุณสามารถพบปัญหาได้โดยขึ้นอยู่กับตัวจัดการที่ใช้ (ไฟล์ memcached, Redis, Custom, ฯลฯ )

ด้วยไฟล์ (ตัวจัดการเริ่มต้น) การรวบรวมขยะจะเกิดขึ้นได้สองวิธี:

  • ระบบที่ใช้เดเบียนส่วนใหญ่ทำ GC ของตัวเองผ่านงาน cron (ซึ่งทำงานได้ดีสำหรับสถานการณ์ของคุณ)
  • distros อื่น ๆ ให้ของ PHP จับกลไก GC เริ่มต้นมันซึ่งจะขึ้นอยู่กับความน่าจะเป็นผลจากแต่ละคำขอเข้ามาเพื่อตรวจสอบ PHP ที่ยื่น mtime session.max_lifetimeอยู่ในเซสชั่นไฟล์และลบเหล่านั้นที่ผ่านมาของพวกเขา ปัญหาเกี่ยวกับวิธีการนี้คือเว็บไซต์ที่มีปริมาณการใช้งานต่ำอาจมีเซสชันอยู่บนเซิร์ฟเวอร์เป็นเวลานานจนกระทั่งมีคำขอเข้ามาเพียงพอ (ขึ้นอยู่กับsession.gc_probabilityคะแนน) เพื่อเรียกใช้ GC เพื่อล้างไฟล์เซสชัน

ด้วยตัวจัดการแบบ memcached และ Redis คุณไม่มีปัญหานี้ พวกเขาจะจัดการล้างหน่วยความจำโดยอัตโนมัติ เซสชั่นอาจยังคงอยู่ในหน่วยความจำกายภาพเป็นเวลาผ่านไปชั่วชีวิต แต่ daemon จะไม่สามารถเข้าถึงได้ หากคุณกังวลเกี่ยวกับเรื่องความปลอดภัยนี้คุณสามารถเข้ารหัสเซสชันของคุณได้ตลอดเวลาหรือค้นหาร้านค้าคีย์ / ค่าที่มีกลไกการล้างหน่วยความจำที่เข้มงวด

ด้วยตัวจัดการเซสชั่นที่กำหนดเองคุณจะต้องสร้างกลไก GC ของคุณเอง ผ่านSessionHandlerInterfaceที่คุณต้องการใช้gcวิธีการที่มือคุณเซสชั่นของช่วงอายุการใช้งานสูงสุดและคุณจะต้องรับผิดชอบในการตรวจสอบถ้าเซสชั่นได้ผ่านอายุการใช้งานขึ้นอยู่กับช่วงเวลานี้และทำเก็บขยะของคุณจากที่นั่น

นอกจากนี้คุณยังสามารถตั้งค่าจุดสิ้นสุดแยกต่างหากที่ตรวจสอบเซสชัน TTL (ผ่านคำขอ AJAX แบบอะซิงโครนัสทางฝั่งไคลเอ็นต์) และส่งการตอบกลับหากเซสชันหมดอายุ (บังคับให้ javascript ยืนยันผู้ใช้อีกครั้ง)


0

ดังนั้น Idea อยู่เบื้องหลัง setInterval และ Sockets สนับสนุน setInterval ในเบราว์เซอร์ส่วนใหญ่และจาวาสคริปต์ WbsocketApi รองรับเกือบทุกเบราว์เซอร์

ภาพรวมโดยย่อ: setInterval () - ลักษณะการทำงานของฟังก์ชั่นนี้จะเกิดขึ้นเมื่อคอมพิวเตอร์ของคุณอยู่ในโหมดสลีป / พัก / โหมดไฮเบอร์เนตจะถูกหยุดชั่วคราวและเมื่อคุณอยู่ในโหมดปลุก

รหัสต่อไปนี้ทำดังต่อไปนี้ในตอนแรก (อาจในเวลาเดียวกัน แต่) มันเริ่ม php server_socket ฟังการเชื่อมต่อ

กว่า javascript websocket api ส่งการประทับเวลาปัจจุบันเป็น Unix มิลลิวินาทีการประทับเวลาในทุก 2 วินาทีคุณสามารถมี 1 วินาทีขึ้นอยู่กับคุณ

หลังจากนั้นซ็อกเก็ตเซิร์ฟเวอร์ php จะได้รับในเวลานี้และตรวจสอบว่ามีอะไรที่เหมือนกับเวลาก่อนหน้าเพื่อเปรียบเทียบเมื่อรหัสเป็นอินสแตนซ์แรก php ไม่มีอะไรที่เหมือนกับเวลาก่อนหน้าเพื่อเปรียบเทียบกับเวลาที่ถูกส่งจาก javascript websocket ดังนั้น php ไม่ทำอะไรเลยนอกจากบันทึกเวลานี้ในเซสชันที่เรียกว่า 'prev_time' และรอให้มีการรับข้อมูลเวลาอื่นจากซ็อกเก็ต javascript ดังนั้นที่นี่จะเริ่มรอบที่สอง เมื่อเซิร์ฟเวอร์ซ็อกเก็ต php ข้อมูลเวลาใหม่จาก javascript WebsocketApi ตรวจสอบว่ามีอะไรที่เหมือนกับเวลาก่อนหน้าเพื่อเปรียบเทียบกับข้อมูลเวลาที่ได้รับใหม่นี้หมายความว่า php ตรวจสอบว่าเซสชันที่เรียกว่า 'prev_time' มีอยู่ในขณะที่เราอยู่ในรอบที่สอง มีอยู่แล้วคว้ามันคุ้มค่าและทำตาม$diff = $new_time - $prev_time, $ diff จะเป็น 2 วินาทีหรือ 2,000 miliseconds เพราะจำวงจร setInterval ของเราเกิดขึ้นในทุก ๆ 2 วินาทีและรูปแบบเวลาที่เราส่งเป็นมิลลิวินาที

กว่า php ตรวจสอบif($diff<3000)ว่ามีความแตกต่างน้อยกว่า 3000 หรือไม่หากรู้ว่าผู้ใช้นั้นทำงานอยู่คุณสามารถจัดการกับวินาทีนี้ได้ตามที่คุณต้องการฉันเลือก 3000 เพราะเวลาแฝงที่เป็นไปได้ในเครือข่ายซึ่งแทบจะเป็นไปไม่ได้ แต่คุณรู้ว่า มันมาถึงเครือข่ายดังนั้นเราจะดำเนินการต่อเมื่อ php พิจารณาว่าผู้ใช้นั้นเป็น php ที่เพิ่งรีเซ็ตเซสชั่น 'prev_time' ด้วยค่า$new_timeที่ได้รับใหม่และเพื่อการทดสอบเท่านั้นมันจะส่งข้อความกลับไปยังซ็อกเก็ต javascript

แต่ถ้า$diffมีมากกว่า 3000 นั่นหมายความว่ามีบางสิ่งที่หยุด setInterval ของเราและมีวิธีเดียวที่มันสามารถเกิดขึ้นได้และฉันคิดว่าคุณรู้แล้วว่าฉันกำลังพูดอะไรอยู่ดังนั้นในelseตรรกะของ ( if($diff<3000)) คุณสามารถออกจากระบบผู้ใช้ ต้องการเปลี่ยนเส้นทางคุณสามารถส่งข้อความไปยังซ็อกเก็ต javacript และสร้างตรรกะซึ่งจะดำเนินการwindow.location = "/login"ขึ้นอยู่กับข้อความนั่นคือนี่คือรหัส:

ก่อนอื่นก็คือไฟล์ index.html เพื่อโหลด javascript:

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

แล้วมันเป็นจาวาสคริปต์มันไม่ได้เข้ารหัสอย่างสวยงามจริงๆ แต่คุณสามารถหาอ่านความคิดเห็นพวกเขามีความสำคัญ:

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 ไม่ต้องกังวลว่าจะมีรหัสเต็มเช่นกัน แต่ส่วนนี้เป็นสิ่งที่ไม่ได้กล่าวถึงข้างต้นงานที่คุณจะได้พบกับฟังก์ชั่นอื่น ๆ แต่พวกเขามีไว้สำหรับถอดรหัสและทำงานร่วมกับซ็อกเก็ตจาวาสคริปต์ อ่านความคิดเห็นที่นี่พวกเขามีความสำคัญ:

<?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 ด้วยคำสั่ง: php -f server_socket.php ไปที่ localhost และทดสอบคอนโซลเปิดเพื่อดูข้อความว่า "คุณกำลังใช้งาน" หรือ "คุณไม่ได้ใช้งาน" (เมื่อคุณมาจากการนอนหลับ); เอ็กซีคิวต์ของคุณจะเกิดขึ้นเมื่อผู้ใช้มาจากโหมดสลีปไม่ใช่เมื่ออยู่ในโหมดสลีปในขณะนั้นทุกอย่างถูกแคชใน pagefile (windows) หรือสลับ (linux)


สร้างโฟลเดอร์และเพียงคัดลอกและวางในไฟล์ที่เรียกใช้ซ็อกเก็ต php ด้วยคำสั่ง: php -f server_socket.php ไปที่localhostและทดสอบคอนโซลเปิดเพื่อดูข้อความว่า "คุณกำลังใช้งาน" หรือ "คุณไม่ได้ใช้งาน" (เมื่อคุณมาจากการนอนหลับ); เอ็กซีคิวต์ของคุณจะเกิดขึ้นเมื่อผู้ใช้มาจากโหมดสลีปไม่ใช่เมื่ออยู่ในโหมดสลีปในขณะนั้นทุกอย่างถูกแคชใน pagefile (windows) หรือในการสลับ (ลินุกซ์)
Dato DT

0

ฉันคิดว่าฉันมีความคิดคุณได้พูดคุยกันมากมายเกี่ยวกับการทำงานของระบบเข้าสู่ระบบ / ออกจากระบบของธนาคาร

กรณีที่ 1: การเข้าถึงเว็บเพจไปยังผู้ใช้โดยไม่ จำกัด เวลาหากผู้ใช้ทำงานอยู่

เมื่อใดก็ตามที่ผู้ใช้ลงชื่อเข้าใช้เริ่มจับเวลาในแบ็กเอนด์ของคุณ (กำหนดเวลาที่คุณต้องการ) สมมติว่า 15 นาที ตอนนี้มันหมายความว่าอะไร? หมายความว่าหากผู้ใช้ไม่ได้ทำกิจกรรมใด ๆ บนหน้าเว็บเราจะออกจากระบบ

ตอนนี้จากหน้าคุณสามารถส่งกิจกรรมของผู้ใช้ไปที่แบ็กเอนด์ของคุณ (สามารถส่งโดยใช้ซ็อกเก็ตหรือการสำรวจความยาว) ซึ่งโดยทั่วไปจะรีเซ็ตตัวจับเวลาและผู้ใช้สามารถใช้หน้าเว็บได้ทุกเวลาที่ต้องการ

หากผู้ใช้ตั้งค่า PC ให้เข้าสู่โหมดสลีปตัวจับเวลาจะไม่รีเซ็ตและคุณสามารถทำให้เซสชันใช้ไม่ได้เมื่อหมดเวลา

หากคุณต้องการที่จะทำให้เซสชั่นผู้ใช้โมฆะทันทีที่พวกเขาทำให้พีซีของพวกเขาในโหมดสลีปคุณสามารถตั้งค่า จำกัด เวลาในการตรวจสอบเซสชั่น ตัวอย่างเช่นเมื่อผู้ใช้ลงชื่อเข้าใช้เราจะสร้างเซสชันซึ่งจะใช้งานได้เพียง 10 วินาทีและเมื่อเราได้รับคำขอกิจกรรมของผู้ใช้เราสามารถรีเซ็ตตัวจับเวลาและให้คีย์เซสชันใหม่

ฉันหวังว่านี่จะช่วยคุณได้ แจ้งให้เราทราบหากคุณมีคำถามใด ๆ


-1

ฉันเขียนสคริปต์เพื่อตรวจสอบว่าเครื่องเข้าสู่โหมดสลีปหรือไม่ แนวคิดคือเมื่อเครื่องอยู่ในโหมดสลีปสคริปต์ทั้งหมดจะหยุดทำงาน ดังนั้นหากเราติดตามเวลาปัจจุบันภายในเวลาช่วงเวลาหนึ่ง ทุกครั้งที่ timeInterval ทริกเกอร์เวลาปัจจุบันลบ (-) เวลาใหม่ควรใกล้เคียงกับ timeInterval มากพอ ดังนั้นหากเราต้องการตรวจสอบว่าตัวจับเวลาไม่ได้ใช้งานเป็นเวลา 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 Authorizers & Redis ฉันไม่สามารถแชร์รหัสในขั้นตอนนี้ แต่ฉันสามารถบอกคุณได้ทุกอย่างเกี่ยวกับวิธีการนำไปใช้กับองค์ประกอบเหล่านี้แนวคิดเดียวกันสามารถนำไปใช้กับผู้อื่นได้ องค์ประกอบที่ไม่ใช่ AWS

ประการแรกเมื่อมีการใช้งานการออกจากระบบที่ไม่มีการใช้งานคุณจะต้องดำเนินการด้านเซิร์ฟเวอร์ราวกับว่ามีใครบางคนปิดคอมพิวเตอร์ของพวกเขาเว็บไซต์ส่วนหน้าจะไม่ออกจากระบบ ฉันใช้แนวคิดของACTIVEผู้ใช้ เมื่อผู้ใช้รับรองความถูกต้องสำเร็จฉันจะเก็บด้วย TTL เป็นเวลา 15 นาทีใน Redis รายการที่มีคีย์ของusername& ค่าของพวกเขาACTIVE(อาจเป็นชื่อผู้ใช้ + เซสชันหากคุณต้องการอนุญาตให้ผู้ใช้ที่กำหนดในเวลาเดียวกันหลายเซสชัน)

ใน Authorizers กำหนดเองของฉันเมื่อผู้ใช้เป็นACTIVEและพวกเขามี Token ที่ถูกต้องผมให้สิทธิ์การเข้าถึงพวกเขาที่จะได้รับการป้องกันทรัพยากรและที่สำคัญที่สุดฉันจะวางอีก Redis กับ&usernameACTIVE

เมื่อใดก็ตามที่ผู้ใช้ล็อกออกฉันเข้าสู่ระบบพวกเขาออกในการแก้ปัญหาการจัดการตัวตนของฉัน (Cognito) INACTIVEและฉันทำเครื่องหมายว่า โปรดทราบว่าหากผู้ใช้ไม่ได้กดปุ่ม API ภายใน 15 นาทีACTIVEผู้ใช้จะไม่สามารถเข้าถึงชื่อผู้ใช้ของพวกเขาได้อีกต่อไปและจะไม่สามารถเข้าถึง API ได้อีกต่อไป & ต้องลงชื่อเข้าใช้อีกครั้งซึ่งพวกเขาจะถูกเปลี่ยนเส้นทางไป

มีหลายสิ่งที่ต้องพิจารณาด้วยวิธีนี้สิ่งหนึ่งที่บ่อยครั้งที่ Authorizers ผลแคชในระยะเวลาหนึ่งและถ้าคุณบอกว่าคุณแคชผลลัพธ์เป็นเวลา 5 นาทีเป็นตัวอย่างผู้ใช้ของคุณอาจถูกล็อกออฟใน 10 นาทีในฐานะผู้ใช้ของคุณ สามารถกดแคชแทน Authorizer ซึ่งจะไม่รีเฟรช ACTIVEรายการ

สิ่งสำคัญคือคุณต้องแน่ใจว่าสิ่งที่คุณใช้ในการจัดเก็บหากผู้ใช้ที่ได้รับคือ ACTIVEนั้นพร้อมใช้งานสูงและจะกู้คืนได้อย่างรวดเร็วในกรณีที่เกิดข้อผิดพลาด

วิธีการใช้ที่เก็บแคชด้วยวิธีนี้คล้ายกับวิธีการตรวจสอบความถูกต้องของโทเค็นที่ไม่ได้รับการดัดแปลงให้เป็นโปรโตคอลการอนุญาตแบบไร้รัฐเช่น OAuth2

เราใช้วิธีนี้มาหลายเดือนแล้วดูเหมือนว่าจะทำงานได้ดีสำหรับเรามันอาจเป็นข้อกำหนดที่น่ารำคาญนิดหน่อยในการจัดการฉันคาดว่าในโลก AWS ว่าจะมีความพร้อมที่จะใช้งาน ของวิธีแก้ปัญหากล่องสำหรับเรื่องนี้ แต่ไม่มีใครพูดถึง


นอกจากนี้เรายังรอดชีวิตจากการทดสอบปากกา (; นี่คือสิ่งที่ความต้องการของเรามาจากตอนแรกใบสมัครของเราเป็นผลิตภัณฑ์ทางการเงินที่ใช้บริการและเราต้องดำเนินการตามความต้องการ
Snickers3192
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.