Dcat-admin实现一个账号只能同时在线一次

具体思路很简单-当用户登录删除上次登录的session并记录现在登录的session id

文档上所写 我们可以重写父类AuthController的方法

namespace App\Admin\Controllers;
use Dcat\Admin\Controllers\AuthController as BaseAuthController;
class AuthController extends BaseAuthController
{
    // 自定义登录view模板
    protected $view = 'admin.login';

    // 重写你的登录页面逻辑
    public function getLogin(Content $content)
    {
        ...
    }

    ...
}

查看继承的父类BaseAuthController,可以看到验证登录的方法


  /**
     * Handle a login request.
     *
     * @param Request $request
     *
     * @return mixed
     */
    public function postLogin(Request $request)
    {
        $credentials = $request->only([$this->username(), 'password']);
        $remember = (bool) $request->input('remember', false);
        /** @var \Illuminate\Validation\Validator $validator */
        $validator = Validator::make($credentials, [
            $this->username()   => 'required',
            'password'          => 'required',
        ]);
        if ($validator->fails()) {
            return $this->validationErrorsResponse($validator);
        }
        if ($this->guard()->attempt($credentials, $remember)) {
          //验证成功后执行响应成功
            return $this->sendLoginResponse($request);
        }
        return $this->validationErrorsResponse([
            $this->username() => $this->getFailedLoginMessage(),
        ]);
    }

验证登录成功后执行sendLoginResponse方法并会生成一个session令牌

 /**
     * Send the response after the user was authenticated.
     *
     * @param \Illuminate\Http\Request $request
     *
     * @return \Symfony\Component\HttpFoundation\Response
     */
    protected function sendLoginResponse(Request $request)
    {
        $request->session()->regenerate();

        return $this->redirectToIntended(
            $this->redirectPath(),
            trans('admin.login_successful')
        );
    }

在新创建的子类里面重写该方法

/**
     * Send the response after the user was authenticated.
     *
     * @param \Illuminate\Http\Request $request
     *
     * @return \Symfony\Component\HttpFoundation\Response
     */
    protected function sendLoginResponse(Request $request)
    {
        //删除旧的会话信息
        $this->delSessionForRedis();
        $request->session()->regenerate();
        //存储新的会话信息
        $this->setSessionForRedis();
        return $this->redirectToIntended(
            $this->redirectPath(),
            trans('admin.login_successful')
        );
    }

    /**
     * 将会话id缓存入redis
     */
    private function setSessionForRedis()
    {
      Redis::set('session_id_'.auth()->id(),'_database__cache:'.session()->getId());
    }

    /**
     * 清理旧会话
     */
    private function delSessionForRedis()
    {
        $key =  Redis::get('session_id_'.auth()->id());
        if($key){
            $key =  '_'.trim($key,"_database_");
            Redis::del($key);
        }
    }

实现了两步删除上一次登录的session、记录当前的session会话id 最后在.env中将SESSION_DRIVER更改为redis驱动

Dcat-admin实现一个账号只能同时在线一次

本作品采用《CC 协议》,转载必须注明作者和本文链接
不成大牛,不改個簽
本帖由系统于 3年前 自动加精
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 25

主要还是异地登录时清除之前的 session,前不久刚做了类似的功能。

目的是不让多地登录,结果发现清掉了 session 后 Dcat 也会退出,因为都是同一会话。

app/Providers/EventServiceProvider.php

protected $listen = [
    // 登录后
    Authenticated::class => [
        LogAuthenticated::class,
    ],
];

app/Listeners/LogAuthenticated.php

// 这里用一个字段存当前活跃中的 sessionId
// 因为 remember_token 字段项目中用不到,我直接存这儿了
// 并且不需要考虑 session 用什么驱动
public function handle(Authenticated $event)
{
    /** @var User $user */
    $user = $event->user;

    // 清除之前的
    session()->getHandler()->destroy($user->remember_token);

    // 存下新的
    $user->remember_token = session()->getId();

    $user->save();
}
2年前 评论
bibi小将 1年前
peterlalala 2年前
lddtime (作者) 2年前
peterlalala 2年前

主要还是异地登录时清除之前的 session,前不久刚做了类似的功能。

目的是不让多地登录,结果发现清掉了 session 后 Dcat 也会退出,因为都是同一会话。

app/Providers/EventServiceProvider.php

protected $listen = [
    // 登录后
    Authenticated::class => [
        LogAuthenticated::class,
    ],
];

app/Listeners/LogAuthenticated.php

// 这里用一个字段存当前活跃中的 sessionId
// 因为 remember_token 字段项目中用不到,我直接存这儿了
// 并且不需要考虑 session 用什么驱动
public function handle(Authenticated $event)
{
    /** @var User $user */
    $user = $event->user;

    // 清除之前的
    session()->getHandler()->destroy($user->remember_token);

    // 存下新的
    $user->remember_token = session()->getId();

    $user->save();
}
2年前 评论
bibi小将 1年前
peterlalala 2年前
lddtime (作者) 2年前
peterlalala 2年前

收藏,点赞,并且觉得很赞。

3年前 评论

点赞 收藏 评论 关注 转发 改进,就差投币了

3年前 评论

再次查看Laravel 文档,发现有 使其他设备上的 session 失效 这个章节,按图索骥,实现对应方法就好.

1年前 评论
Latent (楼主) 1年前

@surest 你别光学习啊,点赞收藏 最好是把所有的都点一遍

3年前 评论

@小李世界 哈哈哈 1楼是我朋友 跟他开玩笑了 :joy:

3年前 评论
小李世界 3年前
VeryCool

那直接登陆的时候生成一个随机令牌存 数据库 里面 实时判断一下....是不是更简单 登陆 修改储存令牌 判断令牌不存在删除session :see_no_evil: :see_no_evil: :see_no_evil: :see_no_evil: :see_no_evil:

3年前 评论

@VeryCool 如何实时判断

3年前 评论
VeryCool 3年前
VeryCool 3年前
VeryCool 3年前
Latent (作者) (楼主) 3年前

@lddtime 这种方式实现这个功能确实很优雅 :+1: :+1: :+1:

2年前 评论
linxianfu

不错不错,点赞收藏一波

2年前 评论

哈哈,学习了,刚好自己想用来着

2年前 评论

dcat-adminguard 不是默认的 web 了。

Redis::set('session_id_'.auth()->id(),'_database__cache:'.session()->getId()); 
=>
Redis::set('session_id_'.$this->guard()->id(),'_database__cache:'.session()->getId());
2年前 评论

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