简体   繁体   English

如何在php中限制每个用户的并发登录数

[英]How to limit the number of concurrent login per user in php

I want to prevent the user's concurrent login based on the admin settings.我想根据管理员设置阻止用户并发登录。

If admin set the concurrent login settings as 3, then the user can login from 3 different places at a time.如果管理员将并发登录设置为 3,则用户可以同时从 3 个不同的地方登录。

If the user attempts to login from 4th place, the application should not allow the user to login.如果用户尝试从第 4 处登录,则应用程序不应允许用户登录。

Any idea how to maintain this via database.知道如何通过数据库维护它。 Please help.请帮忙。 Thanks in advance.提前致谢。

You could maintain a DB-table with active user sessions, where a session is considered active, if last user activity took place less then X minutes ago (configurable value).您可以维护一个包含活动用户会话的数据库表,如果上次用户活动发生在 X 分钟之前(可配置值),则会话被视为活动。

Each time a user tries to authenticate throgh the login form, you should check how many sessions for that user are active at the moment, and based upon that check make a decision whether to authenticate him or decline with some form of response message.每次用户尝试通过登录表单进行身份验证时,您应该检查该用户目前有多少个会话处于活动状态,并根据该检查决定是对他进行身份验证还是使用某种形式的响应消息拒绝。

Fully working example (download) 完整的工作示例(下载)

what you do is that you add an extra column to your users table , name it logins.您所做的是在您的用户表中添加一个额外的列,并将其命名为登录。 Whenever the user logs in increment the column by 1, and in you login script u can check if the number of logins is equal to the limit and when the user logs out then you decrease the no of logins by 1.每当用户登录时将列增加 1,并且在您的登录脚本中,您可以检查登录次数是否等于限制,并且当用户注销时,您将登录次数减少 1。

A problem that might arise using this method is that if the user doesn't log out and the server does not recognize the sessionn again the login is not decremented.使用此方法可能出现的一个问题是,如果用户未注销并且服务器无法再次识别会话,则登录名不会减少。 To avoid this a better way of doing it is below.为了避免这种情况,下面是一种更好的方法。

  1. Create a column name it logins and give it a data type of varchar(500) or preferably text because it might be difficult to predict the size of data we are expecting.创建一个它登录的列名,并为其指定 varchar(500) 数据类型或最好是文本,因为可能很难预测我们期望的数据大小。

  2. when the user logs in, check if the logins column is empty, if it is empty, create a json that contains the session_id, time of login using the time() function.当用户登录时,检查登录列是否为空,如果为空,则使用 time() 函数创建一个包含 session_id 和登录时间的 json。

     if($column_login == '' or count(json_decode($column_login)) == 0){ $login_json = json_encode([ ['session_key'=>'session_key_generated', 'time' => time()] ]); //then update the logins table with the above }
  3. If the logins column is not empty or the count of the column when decoded is greater than zero, then you check if the count is greater than login limit, if the number of logins is not yet greater than the login limit, then append the new session to the logins column in the database table如果登录列不为空或解码时该列的计数大于零,则检查计数是否大于登录限制,如果登录数尚未大于登录限制,则追加新的会话到数据库表中的登录列

    if(count(json_decode($column_login)) > 0 and count(json_decode($column_login)) < $login_limit){ $login_json = json_decode($column_login); $login_json[] = ['session_key'=>'session_key_generated', 'time' => time()]; $login_json = json_encode($column_login); //update the logins column with the new $login_json and log the user in }
  4. If the login limit is reached then check through the logins and check for the one with expired time, for example, a user that is not active of 300secnds is assumed to be logged out, then you delete the session that has expired from the table如果达到登录限制,则检查登录并检查已过期的登录,例如,假定 300 秒不活动的用户已注销,然后从表中删除已过期的会话

    if(count(json_decode($column_login)) >= $limit){ $logins = json_decode($column_login); foreach($logins as $key => $login){ if($login['time'] < time()-300){ //this checks if the iterated login is greater than the current time -300seconds and if found to be true then the user is inactive //then set this current login to null by using the below statement $logins[$key] = null; // or unset($logins[$key]) either should work; } } //after iteration we check if the count of logins is still greater than the limit if(count($logins) >= login_limit){ //then return a login error that maximum logins reached }else{ //then login is successsfull } //update the logins column to equal to json_encode($logins); }
  5. In any request that is been made by a logged user you check if the session key still exists in the login column of the database ($logins['session_key']), and if it is not found then log the user out immediately to avoid escalation of right and if otherwise then update the $login['time'] to the new time().在登录用户发出的任何请求中,您检查会话密钥是否仍然存在于数据库的登录列中($logins['session_key']),如果找不到则立即注销用户以避免权限升级,否则将 $login['time'] 更新为新的 time()。

This method works well.这种方法效果很好。

I have implemented something based on @MaheshKathiriya我已经实现了一些基于@MaheshKathiriya 的东西

I have a table named user having columns 'id' (PK,AI), 'username', 'password','login'我有一个名为user的表,其中包含“id”(PK,AI)、“用户名”、“密码”、“登录”列
serialize() converts an array to a string, and unserialize() converts a serialized string back to an array serialize()将数组转换为字符串, unserialize()将序列化的字符串转换回数组

In login column, I have inserted an array (let's call it main_array) which contains sub arrays.在登录列中,我插入了一个包含子数组的数组(我们称之为 main_array)。
Number of sub arrays in main_array indicates number of registered session in DB. main_array 中子数组的个数表示数据库中注册会话的个数。
The sub array has two values, session_id and time_of_login.子数组有两个值,session_id 和 time_of_login。

Whenever a user logs in we check if the number of sub arrays in main_array.每当用户登录时,我们都会检查 main_array 中子数组的数量。

If main_array is empty, we insert a new sub array with corresponding session_id and time and allow user to login.如果 main_array 为空,我们插入一个具有相应 session_id 和时间的新子数组并允许用户登录。

else if main array is not empty we check how many sub arrays are in it.否则如果主数组不为空,我们检查其中有多少子数组。 If number of sub arrays is more than 0 and less than login_limit than we insert a new sub array and allow user to login.如果子数组的数量大于 0 且小于 login_limit,那么我们将插入一个新的子数组并允许用户登录。

else if number of sub array in main_array is equal to or more than login_limit, then we check for expired session (sub_array['time']>time()-60, means this session was active more than 60 seconds ago)否则,如果 main_array 中子数组的数量等于或大于 login_limit,那么我们检查过期会话(sub_array['time']>time()-60, means this session was active more than 60 seconds ago)
if a session was not active for last 60 seconds we delete it's sub array from main array.如果会话在过去 60 秒内未处于活动状态,我们会从主数组中删除它的子数组。
now we again check if number of sub array in main_array is equal to or more than login_limit, if it's less than login limit, we allow to login and insert new session sub_array to main array.现在我们再次检查 main_array 中子数组的数量是否等于或大于 login_limit,如果它小于登录限制,我们允许登录并将新的会话 sub_array 插入主数组。 else the logins fail (because there are no in active sessions which could replace the current user trying to login) insert a new sub array to the main_array with corresponding session_id and login time.否则登录失败(因为活动会话中没有可以替换当前尝试登录的用户)插入一个新的子数组到 main_array 与相应的 session_id 和登录时间。

<?php
if($_SERVER['REQUEST_METHOD']=='POST'){
  require('db_config.php');
  $session_id = session_id();
  $type = $_POST['type'];
  if($type=='login'){ //if user is trying to login
    $login_limit=3; //this is going to be the limit simultaneous logins
    $username=$_POST['username'];
    $password=$_POST['password'];
    $authenticated = false;

    $sql="select * from `user` where `username` = '$username' and `password` = '$password'";
    $result = $conn->query($sql);
    $count = mysqli_num_rows($result);
    if($count>0){
      $authenticated=true;
      $row = mysqli_fetch_assoc($result);
      $user_id = $row['id'];
    }
    else{
      $authenticated=false;
    }
    if($authenticated){ //doing it this way isn't necessary


      $get_login = "SELECT `login` FROM `user` WHERE  `username` = '$username' and `password` = '$password'";
      $result = $conn->query($get_login);
      $row = mysqli_fetch_assoc($result);
      $login_column = $row['login']; //this contains serialized array of login details;
      if($login_column=='' || count(unserialize($login_column))==0){ //if the column is empty i.e  it's first time login, or there are no users logged in
        echo "login column is empty <br>";
        $sub_array = array('s_key'=>$session_id,'time'=>time());
        $main_array=array($sub_array);

        //update login column
        $main_login = serialize($main_array);
        $update_login = "UPDATE `user` SET `login` = '$main_login' WHERE  `username` = '$username' and `password` = '$password'";
        $isUpdated = ($conn->query($update_login) && ($conn->affected_rows>0));
        if($isUpdated){
          echo "main 1 updated<br>";
          echo "login succesful<br>";
          $_SESSION['isLoggedIn']=true;
          $_SESSION['username']=$username;
          $_SESSION['password']=$password;
        }
        else{
          echo "main 1 not updated <br>";
        }
      }
      else if(count(unserialize($login_column))>0 && count(unserialize($login_column))<$login_limit){ //else if coulmn is not empty but doesn't have max simultaneous logins
        echo "Login column is not empty and has ".count(unserialize($login_column))." logins atm <br> Will insert this login now<br>";
        $main_array = unserialize($login_column);
        $sub_array = array('s_key'=>$session_id,'time'=>time());
        array_push($main_array,$sub_array);

        //update login column
        $main_login = serialize($main_array);
        $update_login = "UPDATE `user` SET `login` = '$main_login' WHERE  `username` = '$username' and `password` = '$password'";
        $isUpdated = ($conn->query($update_login) && ($conn->affected_rows>0));
        if($isUpdated){
          echo "main 2 updated<br>";
          echo "login succesful<br>";
          $_SESSION['isLoggedIn']=true;
          $_SESSION['username']=$username;
          $_SESSION['password']=$password;
        }
        else{
          echo "main 2 not updated <br>";
        }

      }
      else if(count(unserialize($login_column))>=$login_limit){ //else if max login has been reached,
        echo "limit reached in login column, now inactive sessions will be removed and this one will be logged in <br>";
        $logins = unserialize($login_column);
        foreach ($logins as $key => $value) {
          if($value['time']<time()-60){ //change 60 to whatever timelimit you want
            unset($logins[$key]);
          }
          // code...
        }
        //after iteration we check if the count of logins is still greater than the limit
        if(count($logins) >= $login_limit){
            //then return a login error that maximum logins reached
            echo "max logins reached, login FAILED!<br>";
        }else{
            //then login is successsfull
            echo "SUCCESS!<br>";
            $_SESSION['isLoggedIn']=true;
            $_SESSION['username']=$username;
            $_SESSION['password']=$password;

        //update login column
        $main_array = ($logins);
        $sub_array = array('s_key'=>$session_id,'time'=>time());
        array_push($main_array,$sub_array);
        $main_login = serialize($main_array);
        $update_login = "UPDATE `user` SET `login` = '$main_login' WHERE  `username` = '$username' and `password` = '$password'";
        $isUpdated = ($conn->query($update_login) && ($conn->affected_rows>0));
        if($isUpdated){
          echo "main 3 updated<br>";
        }
        else{
          echo "main 3 not updated <br>";
        }
      }

      }
      else{
        echo "something went wrong";
      }
    }
  }
  else if($type == 'logout'){
    // $_SESSION['isLoggedIn']=true;
    $username=$_SESSION['username'];
    $password=$_SESSION['password'];

    $get_login = "SELECT `login` FROM `user` WHERE  `username` = '$username' and `password` = '$password'";
    $result = $conn->query($get_login);
    $row = mysqli_fetch_assoc($result);
    $login_column = $row['login'];

    $searchLogin = unserialize($login_column);
    foreach ($searchLogin as $key => $login) {
      // code...
      if($login['s_key']==$session_id){
        echo "<br> session found: ".$login['s_key']."==".$session_id."<br>";
        unset($searchLogin[$key]);
      }
    }
    //update login column
    $main_array = ($searchLogin);
    // $sub_array = array('s_key'=>$session_id,'time'=>time());
    // array_push($main_array,$sub_array);
    $main_login = serialize($main_array);
    $update_login = "UPDATE `user` SET `login` = '$main_login' WHERE  `username` = '$username' and `password` = '$password'";
    $isUpdated = ($conn->query($update_login) && ($conn->affected_rows>0));
    if($isUpdated){
      echo "logged out<br>";
    }
    else{
      echo "FAILED Logout <br>";
    }


  }
}
?>
<form class="" action="index.php" method="post">
  <input type="text" name="username" value="">
  <input type="password" name="password" value="">
  <input type="hidden" name="type" value="login">
  <button type="submit" name="button">login</button>
</form>
<form class="" action="index.php" method="post">
  <input type="hidden" name="type" value="logout">
  <button type="submit" name="button">logout</button>

</form>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM