如何使用JavaScript创建一个自动注销功能

173 阅读6分钟

使用JavaScript创建一个自动注销功能

在为我们的应用程序实现安全时,我们将在某一时刻检查用户在标签中的活跃度。根据用户的活跃度自动注销,总是一个好的做法。

如果应用程序处理用户的敏感或私人数据,这一点尤其重要。例如,银行账户信息。我们将看到如何根据输入事件(如按键、滚动等)使用JavaScript来做到这一点。

先决条件

要跟上本教程,你应该具备。

  1. 有JavaScript的基本知识。
  2. 具有HTML和CSS的基本知识。
  3. 有PHP的基本知识(可选)。

你可以使用任何技术/语言来编写后端登录脚本。

简要概述

我们将在一个基于PHP作为后台的简单登录界面的帮助下,使用JavaScript研究一个自动注销功能的实现。显示页面将有一个计数器,计算用户因不活动而被注销前的剩余秒数。

如果检测到任何事件,自动注销的计时器将被重置。

开始使用

我们将首先用HTML和CSS为登录和显示屏幕创建用户界面文件。

我不会解释用户界面片段,因为这已经超出了本文的范围,而且也不是本文的主要目的。你可以创建任何你自己的,它并不严格限于我使用的那些。

登录页面代码如下。


<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <link rel="icon" href="assets/images/logo-sacco.png" type="image/x-icon">
  <title>Sign Page | T-Bank</title>

  <!-- Bootstrap CSS CDN -->

<body style="background-color: rgba(46, 51, 52, 1)">
  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css"
    integrity="sha384-9gVQ4dYFwwWSjIDZnLEWnxCjeSWFphJiwGPXr1jddIhOegiu1FwO5qRGvFXOdJZ4" crossorigin="anonymous">


  <!-- Font Awesome JS -->
  <script defer src="https://use.fontawesome.com/releases/v5.0.13/js/solid.js"
    integrity="sha384-tzzSw1/Vo+0N5UhStP3bvwWPq+uvzCMfrN1fEFe+xBmv1C/AtVX5K0uZtmcHitFZ"
    crossorigin="anonymous"></script>
  <script defer src="https://use.fontawesome.com/releases/v5.0.13/js/fontawesome.js"
    integrity="sha384-6OIrr52G08NpOFSZdxxz1xdNSndlD4vdcf/q2myIUVO0VsqaGHJsB0RaBE01VTOY"
    crossorigin="anonymous"></script>
  <link rel="stylesheet" type="text/css" href="/sacco/style/home.css">
  </head>
  <div>
    <nav class="navbar navbar-expand-lg navbar-light" style="background-color: #008081">
      <div class="container-fluid">
        <p>


          <button style="z-index: 2" type="button" id="sidebarCollapse" class="btn btn-info">
            <i class="fa fa-home "></i>
            <span><a href="https://sacco.terrence-aluda.com/">Home Page</a></span>
          </button>
        <div class="d-flex justify-content-center" style="padding: 2%; text-align: center; background-color: #008081">
          <h1 style="color: white">T-BANK</h1>
        </div>
        </p>
      </div>
    </nav>
    <div class="d-flex align-items-center justify-content-center vw-60 vh-70" style="padding-bottom: 3vh">
      <div class="card border-dark  container">
        <div class="card-body border-dark row" style="background-color: #035050">
          <div id="sych" class=" col-md-6 d-flex align-items-center justify-content-center">
            <div class="card-body" style=" min-width: 30vw;">
              <div style="text-align: center; padding-bottom: 15vh" class="card-text text-white">
                <h3>Log in</h3>
              </div>
              <form action="https://sacco.terrence-aluda.com/sacco/backend/eng-ed-auth.php" method="post">
                  <div class="mb-3">
                    <div class="d-flex align-items-center justify-content-center vw-30">
                      <input class="form-control text-center" name="PhoneNo" type="text" autofocus="true" placeholder="Phone number"
                        id="signNum">
                    </div>
                  </div>
                  <div class="mb-3">
                    <div class="d-flex align-items-center justify-content-center vw-50">
                      <input type='password' name="password" class="form-control text-center" placeholder="Confirm password" id="logPass">
                    </div>
                  </div>
                  <div class="mb-3">
                    <div class="d-flex align-items-center justify-content-center vw-50">
                      <span id="banner" class="text-danger"></span>
                    </div>
                  </div>
                  <div class="mb-3" style="text-align: center">
                    <button id="logBtn" class="btn" style="background-color: red; color: white; "><i
                        class="fa fa-unlock-alt"></i> Log in</button>
                  </div>
             </form>
            </div>
          </div>
          <div style="background-image: url(assets/images/logo-big.jpeg); background-size: cover;" class="col-md-6">
            <div style="padding-top: 60vh"
              class="d-flex align-content-center align-items-center justify-content-center vw-100 vh-100">

            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</body>

</html>

这是登录页面,用户在这里输入凭证以登录到系统。样式表是托管的,所以如果你的浏览器由于CORS策略而不能正常渲染UI,你可以点击上面提供的链接查看。

样式表使用了Bootstrap 5。

登录凭证是电话号码 -1234567890 和密码 -1111

输出

Login page

显示页面

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <link rel="icon" href="assets/images/logo-sacco.png" type="image/x-icon">
  <title>Sign Page | T-Bank</title>

  <!-- Bootstrap CSS CDN -->

<body onload="startCountdown()" style="background-color: rgba(46, 51, 52, 1)">
  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css"
    integrity="sha384-9gVQ4dYFwwWSjIDZnLEWnxCjeSWFphJiwGPXr1jddIhOegiu1FwO5qRGvFXOdJZ4" crossorigin="anonymous">


  <!-- Font Awesome JS -->
  <script defer src="https://use.fontawesome.com/releases/v5.0.13/js/solid.js"
    integrity="sha384-tzzSw1/Vo+0N5UhStP3bvwWPq+uvzCMfrN1fEFe+xBmv1C/AtVX5K0uZtmcHitFZ"
    crossorigin="anonymous"></script>
  <script defer src="https://use.fontawesome.com/releases/v5.0.13/js/fontawesome.js"
    integrity="sha384-6OIrr52G08NpOFSZdxxz1xdNSndlD4vdcf/q2myIUVO0VsqaGHJsB0RaBE01VTOY"
    crossorigin="anonymous"></script>
  <link rel="stylesheet" type="text/css" href="/sacco/style/home.css">
  </head>
  <div>
    <nav class="navbar navbar-expand-lg navbar-light" style="background-color: #008081">
      <div class="container-fluid">
        <p>


          <button style="z-index: 2" type="button" id="sidebarCollapse" class="btn btn-info">
            <i class="fa fa-home "></i>
            <span><a href="https://sacco.terrence-aluda.com/">Home Page</a></span>
          </button>
        <div class="d-flex justify-content-center" style="padding: 2%; text-align: center; background-color: #008081">
          <h1 style="color: white">T-BANK</h1>
        </div>
        </p>
      </div>
    </nav>
    <div class="d-flex align-items-center justify-content-center vw-60 vh-70" style="padding-bottom: 3vh">
      <div class="card border-dark  container">
        <div class="card-body border-dark row" style="background-color: #035050">
                      <p class="card-text">
                        <h6 class="text-white">The page will take you to the login page after</h6>&nbsp;<h5 style="color: #0af53a" id="numCount"><h5>&nbsp;<h6 class="text-white">seconds of inactivity.</h6>
                      </p>
        </div>
      </div>
    </div>
  </div>
  </div>
  </div>
</body>

<script src="https://sacco.terrence-aluda.com/sacco/js/autologout.js"></script>

</html>

这是一个显示页面,用户将在这里看到在一段时间不活动后被注销的倒计时。

注意,在body的onload 属性中启动了一个方法。我们将在JavaScript代码中讨论这个问题。

输出

Display page

PHP登录脚本


<?php
require_once "connection.php";

if($_SERVER["REQUEST_METHOD"] == "POST"){

    $password = trim($_POST["password"]);
    $phone = trim($_POST["PhoneNo"]);
    $sql = "SELECT * FROM customers WHERE PhoneNo = '$phone' AND password = '$password'";
        $stmt = $mysqli->query($sql);
            if($stmt->num_rows>0){
                header("Location: https://sacco.terrence-aluda.com/sacco/display.html");
            }
}
$mysqli->close();
?>

这是后端脚本。同样,你可以用你喜欢的任何语言编写。它使用一个POST 方法来接收参数,删除任何尾部的空格,然后运行一个简单的SELECT 查询来检查证书是否存在。

如果凭证没有问题,我们就把用户重定向到显示页面。

三个文件的工作

这三个文件是相连的,用户首先访问登录页面并登录。在PHP脚本验证了用户的身份后,他们被允许访问显示页面。

在设定的非活动时间后,显示页面会重定向到登录页面。

Login -> [Backend aunthentication] -> Display -> (If user is inactive) -> Login

脚本

这就是自动退出功能的 "制造 "过程。

let warningTimeout = 5000;
let warningTimerID;
let counterDisplay = document.getElementById("numCount");
let logoutUrl = "https://sacco.terrence-aluda.com/sacco/eng-edtest.html";

我们首先初始化这些变量。

  1. warningTimeout - 用于存储超时时间。
  2. warningTimerID - 用于存储定时器ID。
  3. counterDisplay - 用于存储将显示计数器数字的元素。
  4. logoutUrl - 用于存储注销后脚本将重定向到的URL地址。
function startTimer() {
  // window.setTimeout returns an ID that can be used to start and stop the timer
  warningTimerID = window.setTimeout(idleLogout, warningTimeout);
  animate(counterDisplay, 5, 0, warningTimeout);
}

这是启动定时器的方法。

window.setTimeout() 返回一个ID,将用于启动和停止计时器。在达到超时后,调用 方法,将用户注销。idleLogout()

使用animate() 方法使计数器成为动画。我们将在接下来的部分中详细讨论这些方法。

计时器被设置为5秒,以节省我们在测试系统时的时间,但你可以适当地改变数值,例如3分钟、200秒等。

由于定时器的持续时间,我们也将动画计数器设置为运行5秒。你要把它设置成与你使用的定时器数值相匹配,如100秒为100,30秒为30,等等。

另一点需要注意的是,由于一些用户体验(UX)问题,建议设置两个计时器,一个用于等待不活动的会话,另一个用于警告用户。

这样,你可以在显示一个带有计数器的模式后,将警告计时器设置为关闭,而不是让计数器在程序的整个时间内运行。例如,10分钟用于等待不活动状态,然后30秒用于显示带有计数器的弹出窗口。

function resetTimer() {
  window.clearTimeout(warningTimerID);
  startTimer();
}
[];

这个建议很直接。它清除了符合超时ID的超时。它在检测到任何事件后被触发。

function idleLogout() {
  window.location = logoutUrl;
}

这个函数只是在一段时间的非活动后重定向到登录页面。

function animate(obj, initVal, lastVal, duration) {
  let startTime = null;

  //get the current timestamp and assign it to the currentTime variable

  let currentTime = Date.now();

  //pass the current timestamp to the step function

  const step = (currentTime) => {
    //if the start time is null, assign the current time to startTime


  //pass the current timestamp to the step function

  const step = (currentTime) => {
    //if the start time is null, assign the current time to startTime

    if (!startTime) {
      startTime = currentTime;
    }

    //calculate the value to be used in calculating the number to be displayed

    const progress = Math.min((currentTime - startTime) / duration, 1);

    //calculate what is to be displayed using the value gotten above

    displayValue = Math.floor(progress * (lastVal - initVal) + initVal);
    obj.innerHTML = displayValue;

    //checking to make sure the counter does not exceed the last value(lastVal)

    if (progress < 1) {
      window.requestAnimationFrame(step);
    } else {
      window.cancelAnimationFrame(window.requestAnimationFrame(step));
    }
  };

  //start animating
  window.requestAnimationFrame(step);
}

简而言之,它获取当前的时间戳和页面加载的时间戳。然后,它利用这两个时间戳之间的差异来计算要显示的内容,并将其显示在元素中。

在我们的例子中,它显示在显示页面的一个h5 元素中。

<h5 style="color: #0af53a" id="numCount"><h5></h5></h5>
function startCountdown() {
  document.addEventListener("mousemove", resetTimer);
  document.addEventListener("mousedown", resetTimer);
  document.addEventListener("keypress", resetTimer);
  document.addEventListener("touchmove", resetTimer);
  document.addEventListener("onscroll", resetTimer);
  document.addEventListener("wheel", resetTimer);
  startTimer();
}

最后,我们有startCountdown() 方法。这是在显示页面上的body的onload 属性中加载时触发的。

当检测到AddEventListener() 方法中包含的事件(mousemove, mousedown, keypress, touchmove, onscroll, wheel)时,resetTimer() 方法被调用以重置计时器,从而使用户保持登录状态。

下面是完整的JavaScript代码。

  let warningTimeout = 5000;
  let warningTimerID;
  let counterDisplay = document.getElementById('numCount');
  logoutUrl = "https://sacco.terrence-aluda.com/sacco/eng-edtest.html";

  function startTimer() {
    // window.setTimeout returns an ID that can be used to start and stop the timer
    warningTimerID = window.setTimeout(idleLogout, warningTimeout);
    animate(counterDisplay, 5, 0, warningTimeout);
  }
    //function for resetting the timer
  function resetTimer() {
    window.clearTimeout(warningTimerID);
    startTimer();
  }

  // Logout the user.
  function idleLogout() {
    window.location = logoutUrl;
  }

  function startCountdown() {}
    document.addEventListener("mousemove", resetTimer);
    document.addEventListener("mousedown", resetTimer);
    document.addEventListener("keypress", resetTimer);
    document.addEventListener("touchmove", resetTimer);
    document.addEventListener("onscroll", resetTimer);
    document.addEventListener("wheel", resetTimer);
    startTimer();
  }
   //the animating function
      function animate(obj, initVal, lastVal, duration) {

        let startTime = null;

        //get the current timestamp and assign it to the currentTime variable

        let currentTime = Date.now();

        //pass the current timestamp to the step function

        const step = (currentTime ) => {

        //if the start time is null, assign the current time to startTime

            if (!startTime) {
            startTime = currentTime ;
            }

        //calculate the value to be used in calculating the number to be displayed

            const progress = Math.min((currentTime  - startTime) / duration, 1);

        //calculate what is to be displayed using the value gotten above

            displayValue = Math.floor(progress * (lastVal - initVal) + initVal);
            obj.innerHTML = displayValue;

        //checking to make sure the counter does not exceed the last value(lastVal)

            if (progress < 1) {
                window.requestAnimationFrame(step);
            }else{
                window.cancelAnimationFrame(window.requestAnimationFrame(step));
            }
        };

        //start animating
        window.requestAnimationFrame(step);
    }

关键研究领域

该代码没有跟踪不同标签中的页面。例如,如果你在不同的标签页中登录了同一个页面,事件只重置了活动标签页中的时间。这意味着其他标签页仍然会将用户注销。我们的目标是重置所有标签页的计时器。

结论

我们研究了在纯JavaScript中创建一个自动注销功能。我们还详细研究了文件和JavaScript代码的工作情况。

用户的私人数据是非常关键的。让别人看不到另一个人的私人信息总是很重要的。自动注销功能是一个很好的解决方案。