一起來實現單使用者登入 —— 功能實現

MArtian發表於2021-12-13

一起來實現單使用者登入 —— 功能實現

保證一個賬號只有一個使用者登入

使用者在訪問網站時,會與網站建立 Session,並將 SessionId 儲存在 Cookie,以此作為使用者此次會話的憑證。

單使用者登入的原理是:在使用者登入後,銷燬 這個使用者之前所有與網站 通訊時建立的 Session

下面我們來一起實現吧!


開啟 .env 檔案,我們將 SESSION_DRIVER 修改為 databse,這樣做的好處是可以沉澱每次與使用者的會話做資料分析,使用者地區分佈,使用者訪問裝置等客戶端資訊。

...
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建立一個使用者,並登入。

一起來實現單使用者登入2


我們開啟資料庫中的 session

一起來實現單使用者登入2

這條記錄就是我們剛剛與網站建立的 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 了。

一起來實現單使用者登入2

單使用者登入功能已經實現,後面我們會給新增 websocket 通訊,來實時通知使用者。

本作品採用《CC 協議》,轉載必須註明作者和本文連結
我從未見過一個早起、勤奮、謹慎,誠實的人抱怨命運。

相關文章