2013-06-06 76 views
1

我想知道是否更好地檢查用戶是否使用他的會話登錄到網站,而不是查詢我的數據庫。檢查用戶是否使用會話登錄,而不是數據庫查詢

如果在數據庫中存在他的ID,那麼爲每個用戶查詢每個子頁面上的數據庫(想到數千個用戶)是不是有點過分了?

如果僅僅檢查存儲在用戶會話中的值,那麼會不會更好,更快並且沒有那麼重的數據庫,而且還有一些條件(如ID必須至少爲10位數字,姓氏必須存在並且應該存在一個由網站操作員所知的字符串)。難道這不能被視爲(99%)安全的方式來檢查用戶是否合法登錄?我讀過PHP以編碼的方式保存會話內容,因此對於想要獲取內容以模擬其他用戶的人來說,它是不可讀的。

+0

您的數據庫是否如此之慢,以至於您真的花費很多時間來查看它?它通常不應該。 –

+2

99%的應用程序都需要從數據庫中獲取某些用戶特定的東西。所以這不是一個額外的查找,而是一個你無法避免的單個查詢。 – arkascha

+0

數據庫被訪問,對吧? – George

回答

2

我相信你的問題存在誤解。會話和數據庫扮演不同的角色,即使他們都是數據存儲。會話是一個臨時存儲,而數據庫是永久存儲。數據保留在數據庫中,直到您明確將其刪除,但會話有過期日期。數據庫和會話之間還有另一個主要區別,叫做session-id。 Session-id是將HTTP請求與服務器上的相應會話數據相關聯的一種方式。會話ids通常在網絡服務器和瀏覽器之間來回傳輸,作爲一個cookie。以下是關於會話如何工作的典型場景:

  1. 瀏覽器的第一個請求已到達Web服務器。服務器上的軟件處理傳入請求,並發現它不包含會話標識(因爲它是第一個請求)。所以爲這個請求創建一個隨機生成的唯一session-id,並通過響應發送回客戶端(不管它可能是什麼)。服務器上還創建了一個與新創建的會話ID關聯的存儲。

  2. 用戶要求在同一臺服務器上有另一頁。這一次,當請求到達服務器時,它帶有一個會話ID,所以不是爲這個請求創建一個新的會話ID,與它相關的數據被加載。如果數據由服務器上的軟件更改,則將響應發送回瀏覽器時將其存儲回存儲器。

  3. 從現在開始,每個發送到Web服務器的請求都會加載相同的會話數據。除非將會話數據或會話標識從服務器中刪除,否則此過程將繼續。

在解釋的場景中,會話用於保持與請求相關聯的數據。會話數據的主要功能之一是存儲用戶的憑證數據。以下是另一種場景:

  1. 用戶打開網站的第一頁。會話ID是爲他創建的,併發送回他的瀏覽器。

  2. 用戶轉到登錄頁面並填寫表單並按下提交按鈕。

  3. 登錄請求已到達服務器。用戶名和密碼是互相檢查的,如果驗證,會話數據中提到此會話ID屬於哪個用戶。

  4. 從現在起,到達服務器的每個請求都會加載會話數據,其中包含此請求來自哪裏,並且與數據庫無關(除非將數據庫用作會話數據存儲)。

這遲到的情況被稱爲authentication,如果請求來自他們是誰聲稱,他們都來自這意味着驗證。在普通情況下,一旦用戶被認證,就不需要再次認證他(除非會話被破壞)。就驗證而言,只有部分數據庫使用是必需的,因爲您需要檢查用戶名和密碼。

此外,還有另一種情況authorization。在這種情況下,你知道誰在問什麼,唯一剩下的就是檢查他是否被允許執行。您知道誰在問,因爲您在會話數據中加入了傳入請求的會話ID的驗證憑據。授權可以分爲兩種類型。首先,您可以檢查並查看是否允許用戶執行請求的操作。其次,您可能需要進一步檢查並查看是否允許用戶對請求的數據執行請求的操作。第一種類型是圖書館的目的,稱爲ACL(訪問控制列表),第二種類型通常在每個項目中分別實施。

ACL是一個函數(簡單來說),它接受請求者用戶和請求的動作(稱爲resource)並返回一個布爾值,指示是否允許用戶執行操作。確切地說,資源可以是複合的(如Files_DeleteFiles_Read)。 ACL功能需要說明誰可以做什麼。大多數開發人員在永久存儲(如數據庫)中加載此數據,因爲用戶已通過身份驗證並將其存儲在會話數據中,以防止從數據庫重新加載該數據。這是可能的,因爲ACL數據並不那麼大,並且可以將它存儲在會話中。因此,通常使用ACL進行授權不需要任何數據庫訪問權限(創建後)。

剩下的唯一討論就是當您要檢查請求者是否允許請求的數據請求的操作。通常在這裏通過數據來表示數據庫的記錄,通常有很多記錄。因此,將大量數據存儲在數據庫本身以外的任何地方都是不合邏輯的。而且由於它已經在數據庫中,所以沒有比SQL更適合查詢誰可以在哪條記錄上做什麼的工具。這是您需要訪問數據庫以驗證用戶請求授權的位置。但在所有其他場景中,會話數據就足夠了。

總之,在所有解釋的場景中,只有一個需要數據庫訪問。其他人只能使用會話數據來完成。

+0

感謝您的深入瞭解。你的回答對我來說似乎很合理。 –

0

雖然從本地文件中獲取會話數據可能會稍微快一點,但使用數據庫對於可伸縮性會更好。

想象一下您使用負載平衡器將流量分離到Web服務器的多個實例的情況。如果數據存儲在數據庫(或集羣)中,則負載均衡器無需擔心哪個Web服務器包含某個請求的會話數據。所有服務器都有它們。這使事情變得更容易。

順便說一句,你可以很容易的兩種方法之間使用session_set_save_handler()

0

如果你的目標僅僅是檢查用戶登錄與否和切換,如果這99%的安全(根據你)是足夠的,那麼當然,你可以做到這一點。但是,如果沒有從外部資源加載(或內部映射,但不太可能),您提到的姓氏檢查是不可能的。

通常,您不僅僅檢查用戶是否已登錄。你加載用戶的個人資料,角色,權限等等......無論如何,你必須打這個數據庫。在大多數情況下,最好在檢查會話的同時加載用戶,並緩存頁面請求的其餘部分的信息,以免過於頻繁地訪問數據庫。

或者,在分佈式環境下使用類似APC用戶緩存存儲或Memcache的東西。

0

您的會話數據在使用session_save_path存儲在服務器的會話數據存儲在其中的人是可讀的一個序列化的文本由session.save_path php.ini文件中或在運行時定義的位置存儲在服務器,如果他們有權訪問服務器。

爲了讓別人劫持用戶會話,他們需要知道session_id。您可以檢查How unique is the php session id瞭解關於會話ID唯一性的更多信息。

要回答您的原始問題,最好使用會話變量來測試以查看用戶是否已通過身份驗證。這可以簡單地通過檢查會話變量的存在來完成,如isset($_SESSION['auth_id'])。當用戶註銷以銷燬會話數據時,請務必使用session_destroy()和session_regenerateid()。

0

當您執行登錄時,您可以爲會話分配一些值。

<?php 
// this should go right at the top of all pages using the session 
session_start(); 

// other code 

// your login procedure 
if (login_condition == true) 
{ 
    $_SESSION['logged_in'] = true; 
    // other session values you want to store 
} 

然後,在您的登錄頁面只有你能做到這一點

<?php 
session_start(); 

if (! isset($_SESSION['logged_in'])) 
{ 
    header("Location: /path/to/login.php\r\n"); 
    exit; 
} 

正如評論所說,如果你需要刪除的用戶,而他們是在會議上什麼呢?您仍然需要一些方法來檢查數據庫的會話。

當然,如果這不是類似於上面的:)

1

如果用戶通過會話登錄檢查關注使用的東西是完全可以接受的,而且是最怎麼做也無妨。在您的登錄代碼中,當用戶成功通過數據庫身份驗證時,可以在會話中設置一個標誌以表示用戶已登錄。在後續頁面中,例如在顯示受保護內容之前檢查此標誌。

// username and password match against the db 
$_SESSION['user'] = array(
    'username' => 'xxx', 
    'userId' => 'xxx', 
    'loggedIn' => true; 
); 

檢查,如果他們登錄:

if(isset($_SESSION['user']) && $_SESSION['user']['loggedIn']) 
{ 
    // good to show protected content 
} 

這樣做並不一定意味着你必須轉儲所有用戶的詳細信息到會話,你仍然可以爲現場查找。