2009-11-13 51 views
14

我想防止在php应用程序中进行多次登录。如何防止PHP网站中的多次登录

首先,我在用户表中创建登录状态(活动,非活动)。

当用户A登录时,用户状态将被设置为'active',如果用户注销,状态将被设置为'notactive'。当另一个客户端使用相同的用户帐户登录时,我检查用户表。如果用户仍然处于活动状态,则会将错误登录信息发送给用户。

发生问题,如果用户关闭浏览器,用户表中的状态可以更新,因为用户没有单击注销。

你对此有什么建议吗?

+0

此问题的实际问题(“我如何注销用户变为不活动?”)已在此处询问过(请参阅http://stackoverflow.com/questions/247412/) – 2009-11-13 08:46:18

回答

17

不是存储用户是否处于活动\不活动状态,最好是存储一些可以针对用户在每个动作基础上检查的属性;如在每次用户尝试执行需要身份验证的操作时,它都会在继续执行之前检查该属性是否匹配。

我建议你做以下事情;

首先,创建一个哈希来唯一标识用户,无论他们登录。我想象一下sha1time()就足以避免冲突。无论您选择什么,请确保其数量足够多,以便其他用户登录时获得相同散列的机会极低(例如,不要散列IP地址或浏览器的用户代理,因为这些散列不会变化足够)。

其次,存储这个散列数据库和用户的session在日志中的时间。这样做会有效地“退出”以前的用户,作为哈希应该是不同的在每次用户登录。

由于我们使用的是会话,因此应该将cookie自动放置在用户的浏览器中,该浏览器将包含一个唯一的ID,以标识用户对他或她的会话数据。 Cookie的内容并不真正值得关注。

接下来,创建一个名为authenticateUser()或类似的函数,在每个脚本开始时调用该函数以确保用户通过身份验证。该脚本应查询数据库,检查具有用户标识的用户是否具有与用户散列匹配的散列。

例如:

function authenticateUser($id, $hash, $databaseLink) { 
    # SQL 
    $sql = 'SELECT EXISTS(
       SELECT 1 
       FROM `tbl_users` 
       WHERE `id` = \''.mysql_real_escape_string($id).'\' 
       AND `hash` = \''.mysql_real_escape_string($hash).'\' 
       LIMIT 1 
      );'; 

    # Run Query 
    if ($query = mysql_query($sql, $databaseLink)) { 
     # Get the first row of the results 
     # Assuming 'id' is your primary key, there 
     # should only ever be one row anyway.  
     $result = mysql_fetch_row($query); 

     # Casting to boolean isn't strictly necessary here 
     # its included to indicate the mysql result should 
     # only ever been 1 or 0. 
     return (bool)($result[0]); 
    } else { 
     # Query error :(
     return false; 
    } 
} 

然后,我们简单地传递authenticateUser()用户的IDhash(根据您的会话数据)和database link(你将不得不刚才打开的数据库连接)。

如果authenticateUser()返回true,则认证用户。如果false,用户不是或数据库不可用或有SQL错误。

但请注意,这会增加您的服务器负载,因为每个页面请求发送一次数据库请求。在任何时候都有数千人登录的大型项目上进行这项工作可能不是那么明智。我相信有人可以提出改进建议。

另外,等待Cookie过期并不是强制注销非活动用户的最佳方式,因为您绝对不应该信任Cookie。相反,您可以添加名为last_active的列,每次用户通过身份验证时都可以进行更新。这也会增加服务器负载,但是可以通过为已停用3小时的用户删除hash来手动覆盖过时的登录。

+0

nness - 是否足以将phpsessid存储在表和$ _SESSION中?我表明,当同一个帐户在不同的系统下登录时,会建立一个新的phpsessid。 – 2012-12-10 16:37:50

+0

我想这可能是适合的。不要太难测试,请从许多设备登录并观察是否注销了正确的设备。 – 2012-12-11 00:13:20

+0

它似乎运作良好。在我的应用程序沙箱中实施,并在同一个帐户上使用少量系统进行测试。工作得很好。 – 2012-12-11 12:45:49

6

你应该做的是检查它们在尝试登录时是否在最后几分钟处于活动状态。 这可以使用lastonline标记来完成,并且应该在用户表中的每个页面请求上进行设置。

如果不使用JavaScript,则可以在登录时检查用户是否在最后15分钟内处于活动状态。如果没有,您可以作为新用户登录。

你也可以用javascript来做到这一点。做一个每分钟左右都会触发的ajax调用。

<script> 
setInterval(function() { 
    // do the ajax call 
}, 60000); 
</script> 

让这个调用转到将编辑用户数据库中lastonline戳的脚本。 当试图登录时,检查用户数据库,如果lastonline戳超过了分钟,并且您检查了是否可以登录。当您在页面上时,这会有所帮助,但在最后15分钟内您不活跃,并且您不希望别人登录。

+0

为什么这不起作用?我不明白 – 2009-11-13 08:58:23

+0

我并不是downvoter,但拥有客户端JavaScript绝不是保证访客将在60秒后注销的保证。还要考虑如果用户简单地关闭页面会发生什么。 – 2009-11-13 09:04:30

+0

我会帮忙一下。 但是,如果用户终止浏览器? – 2009-11-13 09:48:09

5

你可以改变你的模型,以便只有最新的用户可以登录。

如果录制看到每个用户的最近一次会议ID,登录时,第二次就可以目前垃圾任何现有的会话,有效地注销它们。

对于普通用户来说,事情似乎是“正常工作”。如果您想防止“异常”用户分发他们的登录凭据,这应该成为一种抑制措施。

+0

老实说,我没有找到答案。我会给你更具体的问题:D。 Person_A尝试使用User_A登录进行登录,用户状态将被设置为活动 和后者,Person_B想要尝试使用用户A登录到Webiste。由于User_A仍处于活动状态,因此person_B无法登录到Web。 '当一个帐户仍然有效时,另一个人无法通过使用相同用户登录到该网站,并且发生问题。如果用户终止浏览器。浏览器无法将数据发送到服务器以执行注销操作。因此,状态将始终处于活动状态,但用户不再活动。 – 2009-11-13 09:16:28

+2

那么,我说的是,当user_B登录时,user_A会自动注销。为什么要阻止user_B的登录可能有一个很好的理由,但是你还没有明确说明为什么这是更可取的。我的建议解决了等待会话超时以允许重新登录的问题,同时还阻止了用户登录两次。 – 2009-11-13 10:11:14

0

使用客户端JavaScript为了跟踪登录用户是不可靠的。

通过简单地在db中创建lastlogindate字段并使用用户的最后登录时间戳更新它,可以获得相同的结果。

在每一次登录尝试中,如果now() - $ lastlogindate> predefined_timeout,那么你应该接受新的登录,否则拒绝它。

1

您需要创建一个唯一的ID并将其存储在数据库中。我所做的也是创造。我将其中一个存储在会话变量中,并使用它来防止会话劫持,并在数据库中阻止另一个会阻止多次登录。下面的代码将创建一个唯一的ID:

$unique_id = sha1('xzr4'.gethostbyaddr($_SERVER['REMOTE_ADDR']).$random_string.$_SERVER['HTTP_USER_AGENT'].'f8k2'); 

如果唯一ID不匹配,你只需登录用户了。

4

下面是需要不断数据库访问工作的一个解决方案...

(这将避免要求您每次请求的时间来检查SESSION_ID()对数据库值/刷新一页,减轻数据库/服务器压力)...

1。在登录,抢存储在数据库中该用户的预先存在的SESSION_ID并做到这一点:

session_id("the pre-existing session id in the database goes here"); 
session_start(); 
session_destroy(); 

2.然后开始一个新的会话和这个新的session_id保存到数据库,覆盖前一个。这将注销此用户的前一个会话,如果有一个活动(有效地使用此帐户注销其他人)。

试一试,让我知道如果这是伎俩!

+0

我刚试过 - 很好地工作,没有强调服务器。非常优雅 - 谢谢! – cronoklee 2014-09-11 22:10:36

+0

但session_regenerate_id()函数呢?因为我定期使用它?所以它似乎我必须保存/更新重新生成的ID到数据库以及因此使相同的压力分贝权利? – 2016-10-20 11:28:17