保證一個賬號只有一個使用者登入
使用者在訪問網站時,會與網站建立 Session
,並將 SessionId
儲存在 Cookie
,以此作為使用者此次會話的憑證。
單使用者登入的原理是:在使用者登入後,銷燬 這個使用者之前所有與網站 通訊時建立的
Session
。
下面我們來一起實現吧!
開啟 .env
檔案,我們將 SESSION_DRIVER
修改為 databse
,這樣做的好處是可以沉澱每次與使用者的會話做資料分析,使用者地區分佈,使用者訪問裝置等客戶端資訊。最直白的作用就是可以通過這些使用者的失效 session
分析使用者登入地址,看看哪些使用者在分享賬號。
...
SESSION_DRIVER=database // 修改為 database
SESSION_LIFETIME=120
...
假設你已經配置好了資料庫,我們既然要將 session
儲存在資料庫,那麼還需要建立一個 sessions
表, Laravel 已經非常貼心的為我們準備好了。
執行命令:
php artisan session:table
Laravel 已經為我們生成了遷移檔案,我們直接執行遷移命令來建立資料表就好
php artisan migrate
使用者登入
下面我們來完成使用者登入功能,執行命令來建立腳手架
php artisan ui:auth
訪問 your-project-url/register
建立一個使用者,並登入。
我們開啟資料庫中的 session
表
這條記錄就是我們剛剛與網站建立的 Session
id | user_id | ip_address | user_agent | payload | last_activity |
---|---|---|---|---|---|
服務端Session | 關聯的 user 表 ID | 訪問地址 | 終端裝置 | 前端加密的SessionID | 最後活躍時間 |
銷燬之前的 Session
我們已經生成了使用者腳手架,開啟 app/Http/Controllers/Auth/LoginController.php
檔案。Laravel
的登入功能依賴於 AuthenticatesUsers
Trait。
// LoginController.php
class LoginController extends Controller
{
...
use AuthenticatesUsers; // 開啟這個 trait
...
我們來開啟這個檔案看看,往下找到 authenticated
方法
/**
* The user has been authenticated.
*
* @param \Illuminate\Http\Request $request
* @param mixed $user
* @return mixed
*/
protected function authenticated(Request $request, $user)
{
//
}
這個方法的觸發節點是 使用者授權成功之後,寫入 Session 之前,那我們可以通過它來銷燬已登入使用者之前的所有 Session
。
把它複製一下,然後回到 LoginController
檔案,將 authenticated
方法覆寫成我們自己的業務邏輯。
protected function authenticated(Request $request, $user)
{
DB::table('sessions')->where('user_id', $user->id)->delete()
}
我們可以簡單粗暴的直接將使用者之前登入 Session
刪除,但是這樣做並沒有收集到使用者資訊,建立 Session
表就沒意義了。
其實只需要將 session
表儲存的 id
進行修改,使其與前端傳輸的 SessionID
不匹配,就代表會話失效,所以我們稍微修改一下
protected function authenticated(Request $request, $user)
{
DB::table('sessions')->where('user_id', $user->id)
->update([
'id' => DB::raw("concat('OUTMAN_', user_id, '_', id)"),
'user_id' => null,
]);
}
將 id
新增 OUTMAN
字首,並拼接 user_id
欄位,這樣既達到了 Session 失效,又保留了使用者資訊。
你可能會疑問,既然我們只更新資料,那為什麼不直接使用 軟刪除
呢?
其實 sessions
表不只儲存已登入使用者的 session
,在使用者訪問網站那一刻,就已經建立了 session
,如果使用者沒有登入,那麼這條記錄其實對我們來講其實沒有意義。
回到瀏覽器,開啟一個 視窗A
,再使用快捷鍵 Ctrl+Shift+N
建立一個無痕 視窗B
,達到 Session 隔離的目的。
先在 視窗A
登入賬號,再到 視窗B
登入相同的賬號,然後再回到 視窗A
,按 F5 重新整理一下頁面,發現 視窗A
的使用者已經退出。說明我們剛剛的程式碼生效了,因為在 視窗B
登入的時候,執行了查詢,並把 視窗A
的 session 更新了。
我們再來看一下 sessions
表,已經有一個失效的 Session
了。
單使用者登入功能已經實現,後面我們會給新增 websocket 通訊,來實時通知使用者。
本作品採用《CC 協議》,轉載必須註明作者和本文連結