一般登入流程
传统登入方式是透过帐号密码登入后,透过 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,没有的话则表示未登入 |
帐号登入必要条件
要确认帐号有登入有两个条件
- 使用者含有 session_id 的 Cookie 存在浏览器
- Server 的 Session 中有此 session_id
要确保 Client 端的含有 session_id 的 Cookie 一直存在,这样 Server 才能有 session_id 资料去做验证
而且 Server 的 Session 有此 session_id 的使用者资料
Session 限制
因为 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,也可以让使用者可以正常登入
remember_token 安全性
因为只要 Cookie 过期时间设定的够长,只要一直有 remember_token
,则使用者就可以一直无限期的一直不断的维持登入状态,这样可能会有安全性的问题,所以在使用者自己触发登出时,记得更新储存在资料库的 remember_token,让其他有此 remember_token 的使用者无法继续登入
Laravel 记住登入范例
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
纪录 Remember Token 的 Cookie
在 Laravel 原生记录 Remember Token vendor/laravel/framework/src/Illuminate/Auth/SessionGuard.php
程式中,在 queueRecallerCookie()
会纪录此 Rememver Token
- 第 1 个变数
$user->getAuthIdentifier()
是 user_id - 第 2 个变数
$user->getRememberToken()
是资料库的 remember_token - 第 3 个变数
$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;
}
参考资料
- security - What is the best way to implement “remember me” for a website? - Stack Overflow
- Implementing Secure User Authentication in PHP Applications with Long-Term Persistence (Login with “Remember Me” Cookies) - Paragon Initiative Enterprises Blog
- API Authentication Workflow with JWT and Refresh Tokens - DEV Community
- Authentication - Laravel - The PHP Framework For Web Artisans
- session、cookie与"记住我的登入状态"的功能的实现 | 程式前沿
Donate KJ 贊助作者喝咖啡
如果這篇文章對你有幫助的話,可以透過下面支付方式贊助作者喝咖啡,如果有什麼建議或想說的話可以贊助並留言給我
If this article has been helpful to you, you can support the author by treating them to a coffee through the payment options below. If you have any suggestions or comments, feel free to sponsor and leave a message for me!
方式 Method | 贊助 Donate |
PayPal | https://paypal.me/kejyun |
綠界 ECPay | https://p.ecpay.com.tw/AC218F1 |
歐付寶 OPay | https://payment.opay.tw/Broadcaster/Donate/BD2BD896029F2155041C8C8FAED3A6F8 |