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年前 自动加精
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
讨论数量: 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年前

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

3年前 评论

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

3年前 评论

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

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年前

主要还是异地登录时清除之前的 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年前

@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年前 评论

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

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

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