2009-11-13 64 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