一起来实现单用户登录 —— 功能实现

一起来实现单用户登录 —— 功能实现

保证一个账号只有一个用户登录

用户在访问网站时,会与网站建立 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创建一个用户,并登录。

一起来实现单用户登录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 协议》,转载必须注明作者和本文链接
悲观者永远正确,乐观者永远前行。
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 5

系列已读完,但有个问题。

id 添加 OUTMAN 前缀,并拼接 user_id 字段,这样既达到了 Session 失效,又保留了用户信息。

这里说要保留用户信息,采用修改而不是删除,但后文并没有地方用到这些信息。

保留这些失效 session 的目的什么?

2年前 评论
MArtian (楼主) 2年前
MArtian (楼主) 2年前
lddtime (作者) 2年前

楼主有没有试过多个 guard 情况

2年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!