JWT Token 記住登入帳號 Remember me
驗證進階知識: JWT Token 記住登入帳號
傳統登入方式是透過帳號密碼登入後,透過 cookie
中的 session_id
去與 Server 的 session
資料去做比對,如果有比對到 session
資料的話,會從儲存在 session
中的使用者編號,去資料庫或快取撈取使用者的資料,以達到登入該使用者的流程
步驟 | 流程 | 用途 |
---|---|---|
1 | 傳入帳號密碼驗證 | 驗證帳號密碼是否正確,資料庫是否有此帳號 |
2 | 記錄使用者資料及 session_id 到 Session | 將登入使用者的資料儲存在 Session,之後其他 Request 可以直接取用,避免敏感資料讓其他使用者資料 |
3 | 記錄使用者 session_id 到 Cookie | 告訴前端此使用者的身份是誰,之後會透過這個含有 session_id 的 cookie 做驗證 |
4 | 透過 session_id 的 Cookie 做其他驗證請求 | 撈取 Cookie 中的 session_id 驗證 Server 的 Session 是否有此 session_id,沒有的話則表示未登入 |
要確認帳號有登入有兩個條件
要確保 Client 端的含有 session_id 的 Cookie 一直存在,這樣 Server 才能有 session_id 資料去做驗證
而且 Server 的 Session 有此 session_id 的使用者資料
因為 Cookie 是存在使用者自己的瀏覽器端,所以要確保 Client 端的含有 session_id 的 Cookie 一直存在比較容易,看系統安全性的情境,可以將 Cookie 的過期時間設定很長即可,例如 1 年、3 年、5 年之類的,因為存再久也只是存在使用者自己的瀏覽器,對於 Server 是幾乎沒有任何負擔的
額外的負擔應該就是每次 Request 都會把 cookie 傳送到 Server,Cookie 越多的話,每個 Request 需要傳送的資料就會越多,造成 Request 變得比較肥大,但這個就是取捨
但 Session 因為是存放在 Server 端,所以如果要完整地將使用者資料保留 1 年、3 年、5 年之類的負擔會很大,如果使用者一年只登入一次,我們卻要將他資料保留這麼久都沒用到,而當使用者越來越多達到百萬千萬級時,一個 Session 檔案雖然只有幾 k,但一乘以百萬千萬來說,對於硬體的儲存負擔還是很大的
所以 Session 的資料通常會依照使用者的情境,不會將 Session 儲存太久
為了減輕 Server 儲存 Session 的壓力,會在使用者資料表加入一個 remember_token
的欄位,當作幫使用者做重新產生 Session 登入的動作,所以登入流程會變成
步驟 | 流程 | 用途 |
---|---|---|
1 | 傳入帳號密碼驗證 | 驗證帳號密碼是否正確,資料庫是否有此帳號 |
2 | 記錄使用者資料及 session_id 到 Session | 將登入使用者的資料儲存在 Session,之後其他 Request 可以直接取用,避免敏感資料讓其他使用者資料 |
3 | 記錄使用者 session_id 到 Cookie | 告訴前端此使用者的身份是誰,之後會透過這個含有 session_id 的 cookie 做驗證 |
4 | 產生新的 remember_token 記錄到使用者資料表的 remember_token 欄位 |
做為之後驗證重新登入用 |
5 | 記錄使用者 remember_token 及 user_id 到 Cookie | 當 Cookie 中的 session_id 找不到 Session 時,會用 user_id 及 remember_token 去驗證登入 |
6 | 透過 session_id 的 Cookie 做其他驗證請求 | 撈取 Cookie 中的 session_id 驗證 Server 的 Session 是否有此 session_id,沒有的話則表示未登入 |
7 | 透過 remember_token 及 user_id 的 Cookie 做重新登入 | 撈取 Cookie 中的 remember_token 及 user_id,與資料庫做比對,確認是否 token 合法可以正常登入,登入成功重新產生 Session |
當 Session 保留時間很短時(例如 20 分鐘),在 Session 移除時,也可以透過 remember_token 的 Cookie 去做重新登入的動作,並重新產生使用者的 Session,確保存放在 Server 中的 Session 都是近期登入的活躍使用者,確保不會有沒在使用的 Session,也可以讓使用者可以正常登入
因為只要 Cookie 過期時間設定的夠長,只要一直有 remember_token
,則使用者就可以一直無限期的一直不斷的維持登入狀態,這樣可能會有安全性的問題,所以在使用者自己觸發登出時,記得更新儲存在資料庫的 remember_token,讓其他有此 remember_token 的使用者無法繼續登入
Laravel 版本 8.x
在 Laravel 原生記錄使用者 vendor/laravel/framework/src/Illuminate/Auth/SessionGuard.php
程式中,在 login()
函式的第二個參數是 $remember = false
,若傳入 true
則會對使用者進行記錄登入帳號 token 的流程
// vendor/laravel/framework/src/Illuminate/Auth/SessionGuard.php
/**
* Log a user into the application.
*
* @param \Illuminate\Contracts\Auth\Authenticatable $user
* @param bool $remember
* @return void
*/
public function login(AuthenticatableContract $user, $remember = false)
{
// If the user should be permanently "remembered" by the application we will
// queue a permanent cookie that contains the encrypted copy of the user
// identifier. We will then decrypt this later to retrieve the users.
if ($remember) {
// 確保資料庫有此 remember_token,若沒有則產生新的
$this->ensureRememberTokenIsSet($user);
// 紀錄 remember_token 到 cookie
$this->queueRecallerCookie($user);
}
}
在 ensureRememberTokenIsSet()
函式會確保資料庫有產生 remember_token
,如果沒有的話則會重新產生一個 remember_token
儲存至資料庫
產生完 remember_token
後,則呼叫 queueRecallerCookie()
紀錄 remember_token 到 cookie
在 Laravel 原生記錄 Remember Token vendor/laravel/framework/src/Illuminate/Auth/SessionGuard.php
程式中,在 queueRecallerCookie()
會紀錄此 Rememver Token
$user->getAuthIdentifier()
是 user_id$user->getRememberToken()
是資料庫的 remember_token$user->getAuthPassword()
是使用的密碼第 3 個參數在 Laravel 8.x 版本都沒有用到,不確定紀錄這個用途是要幹嘛
// vendor/laravel/framework/src/Illuminate/Auth/SessionGuard.php
/**
* Queue the recaller cookie into the cookie jar.
*
* @param \Illuminate\Contracts\Auth\Authenticatable $user
* @return void
*/
protected function queueRecallerCookie(AuthenticatableContract $user)
{
$this->getCookieJar()->queue($this->createRecaller(
$user->getAuthIdentifier().'|'.$user->getRememberToken().'|'.$user->getAuthPassword()
));
}
在使用 auth()->user()
撈取登入使用者資料時,會先使用 Session 試著撈取登入使用者的資料,當撈取不到 Session 中使用者的資料時,如果有 Remember Token,則會使用 Remember Token 去做登入
// vendor/laravel/framework/src/Illuminate/Auth/SessionGuard.php
/**
* Get the currently authenticated user.
*
* @return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function user()
{
// 取得 Session 中的使用者編號,使用 Session 登入
$id = $this->session->get($this->getName());
// First we will try to load the user using the identifier in the session if
// one exists. Otherwise we will check for a "remember me" cookie in this
// request, and if one exists, attempt to retrieve the user using that.
if (! is_null($id) && $this->user = $this->provider->retrieveById($id)) {
$this->fireAuthenticatedEvent($this->user);
}
// 使用 Remember Token 登入使用者(如果有的話)
// If the user is null, but we decrypt a "recaller" cookie we can attempt to
// pull the user data on that cookie which serves as a remember cookie on
// the application. Once we have a user we can return it to the caller.
if (is_null($this->user) && ! is_null($recaller = $this->recaller())) {
$this->user = $this->userFromRecaller($recaller);
if ($this->user) {
$this->updateSession($this->user->getAuthIdentifier());
$this->fireLoginEvent($this->user, true);
}
}
return $this->user;
}
驗證進階知識: JWT Token 記住登入帳號