柔晶美网络工作室

柔晶美网络工作室,倾心于web技术的博客站点

关注我 微信公众号

您现在的位置是: 首页 > 博客日记

Laravel8.x 实现单用户登录,异地登录自动退出当前账号的方法

2021-06-03 admin laravel  1257

需求:用户只能在一台设备上登录,如果同一账号在另一个设备上登录,已登录的设置会自动退出。实现方法,使用Laravel的中间件,具体如下:

一、创建单用户中间件

php artisan make:middleware SingleLoginMiddleware

系统会生成一个文件 app/Http/Middleware/SingleLoginMidleware.php,把他修改成下面的样子


namespace App\Http\Middleware;

use Closure;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Redis;

class SingleLoginMiddleware
{
    /**
     * Handle an incoming request.
     * @param $request
     * @param Closure $next
     * @param null $guard
     * @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\RedirectResponse|mixed|\Symfony\Component\HttpFoundation\Response
     */
    public function handle($request, Closure $next, $guard = null)
    {
        if (Auth::guard($guard)->guest()) {
            if ($request->ajax() || $request->wantsJson()) {
                return response('Unauthorized.', 401);
            } else {
                return redirect()->guest('/login');
            }
        }


        if ($this->isRelogin($request)) {
            //清空登录数据, 重定向
            $request->session()->flush();
            $request->session()->regenerate();
            return redirect()->guest('/login');
        }
        return $next($request);
    }

    /**
     * 判断用户是否 重复登录
     * @param $request
     * @return bool
     */
    protected function isRelogin($request)
    {
        $user = Auth::user();
        if ($user) {
            $cookieSingleToken = session('SINGLE_TOKEN');
            if ($cookieSingleToken) {
                // 从 Redis 获取 time
                $lastLoginTimestamp = Redis::get('SINGLE_TOKEN_' . $user->id);
                // 重新获取加密参数加密

                $ip = $request->getClientIp();
                $redisSingleToken = md5($ip . $user->id . $lastLoginTimestamp);

                if ($cookieSingleToken != $redisSingleToken) {
                    //认定为重复登录了
                    return true;
                }
                return false;
            }
        }
        return false;
    }
}

二、注册单用户中间件

注册 SingleLoginMiddleware 到 kernel。打开 path/singleLogin/src/app/Http/Kernel.php

//在下面添加singleLogin一行
protected $routeMiddleware = [
        'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
        ...
        \Illuminate\Routing\Middleware\ThrottleRequests::class,
        'auth.singleLogin' => \App\Http\Middleware\SingleLoginMiddleware::class,
    ];

三、配置中间件保护URL

设置 SingleLoginMiddleware 来保护 /home url,修改 routes/web.php ,修改后如下

Route::get('/', function () {
    return view('welcome');
});

Auth::routes();


//设置使用中间件来保护特定的url
Route::group(['middleware' => 'auth.singleLogin'], function() {
    # 用户登录成功后的路由
    Route::get('/home', 'HomeController@index');
});

四、修改 app/Http/Controllers/HomeController.php 删掉部分代码, 修改后如下

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class HomeController extends Controller
{
    /**
     * Show the application dashboard.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        return view('home');
    }
}

五、重写登录功能

验证登录操作我们还需要一个登录功能,建立 app/Foundation/SingleLoginAuthenticatesUsers.php 文件内容如下,这个文件的主要目的是改写系统生成的 src/vendor/laravel/framework/src/Illuminate/Foundation/Auth/AuthenticatesUsers.php
traits 的一个登录后方法,,以实现我们登录之后的一些处理。

namespace App\Foundation;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Redis;

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

        $this->clearLoginAttempts($request);
        #这里来做登录后的操作
        $this->singleLogin($request);
        return $this->authenticated($request, $this->guard()->user())
            ?: redirect()->intended($this->redirectPath());
    }

    /**
     * 执行单用户登录, 存储必要数据
     * @param Request $request
     * @throws \Exception
     */
    protected function singleLogin(Request $request)
    {
        try {
            $timeStampNow = time();
            $userLoginIp = $request->getClientIp();
            $user = Auth::user();
            $singleToken = md5($userLoginIp . $user->id . $timeStampNow);
            Redis::set('SINGLE_TOKEN_' . $user->id, $timeStampNow);
            session(['SINGLE_TOKEN' => $singleToken]);
        } catch (\Exception $exception) {
            throw new \Exception($exception);
        }
    }
}

然后我们修改 app/Http/Controllers/Auth/LoginController.php 中关于上面AuthenticatesUsers trait 的引用的地方, 修改后如下,注意别忘了引入命名空间

    use AuthenticatesUsers, SingleLoginAuthenticatesUsers{
        SingleLoginAuthenticatesUsers::sendLoginResponse insteadof AuthenticatesUsers;
    }

这样我们就替换掉了系统中的 sendLoginResponse方法取而代之的是我们 SingleLoginAuthenticatesUsers 中定义的 sendLoginResponse 方法

六、测试结果

分别使用两个浏览器访问 localhost/home,第一浏览器登录之后, 再去第二浏览器进行登录,然后再回到第一个浏览器的 localhost/home 刷新一下 发现跳转到了 localhost/login,这样就完成简单的单用户登录功能。

文章评论


需要 登录 才能发表评论
热门评论
0条评论

暂时没有评论!