需求

一个用户不能重复登录. 后登录者可以踢掉前者.

设计思路:

核心概念

用户ID: 是用户表主键
singleToken 算法:
singleToken = md5(用户IP + 用户ID + 登录的Unix时间戳) SESSION 中存储一份 SESSION_SINGLE_TOKEN REDIS 中存储一份 登录的Unix时间戳
REDIS_SINGLE_TOKEN = 根据REDIS中登录时间戳运算后得到token 用户访问时:
如果 SESSION_SINGLE_TOKEN != REDIS_SINGLE_TOKEN
那么 认为重复登陆,销毁登录信息,跳转到登录页面

流程描述

  1. 用户登录的时候使用用户IP+用户表主键+Unix时间戳组成的字符串, 经过md5运算生成一个singleToken 字符串. 并且存入session.

  2. 在redis中保存登录时的 Unix时间戳,redis中保存的内容应该有过期时间, 通常和session过期时间一致.

    key : SINGLE_TOKEN + 用户id
    value : 登录时 unix时间戳
  3. 每一次用户请求需要登录验证的url, 那么用session中的singleToken 和 经过md5运算的 登录IP+用户ID+redis中的Unix时间戳 字符串作比较.

    如果一致那么方可访问.

    如果redis中的时间戳为空,那么只是返回login页面.

    如果redis中时间戳不为空且两个计算后的token不一致那么说明两个账户同时登录了, 那么返回login画面并提示您的账户在其他位置登录,不能重复登录之类的消息.

开始实现:

建立测试项目(准备工作)

为了展示我们的功能, 创建一个名为singleLogin的新项目.

composer create-project --prefer-dist laravel/laravel singleLogin

创建系统自带的认证

php artisan make:auth
php artisan route:list
+--------+----------+------------------------+----------+------------------------------------------------------------------------+--------------+
| Domain | Method | URI | Name | Action | Middleware |
+--------+----------+------------------------+----------+------------------------------------------------------------------------+--------------+
| | GET|HEAD | / | | Closure | web |
| | GET|HEAD | api/user | | Closure | api,auth:api |
| | GET|HEAD | home | | App\Http\Controllers\HomeController@index | web,auth |
| | GET|HEAD | login | login | App\Http\Controllers\Auth\LoginController@showLoginForm | web,guest |
| | POST | login | | App\Http\Controllers\Auth\LoginController@login | web,guest |
| | POST | logout | logout | App\Http\Controllers\Auth\LoginController@logout | web |
| | POST | password/email | | App\Http\Controllers\Auth\ForgotPasswordController@sendResetLinkEmail | web,guest |
| | GET|HEAD | password/reset | | App\Http\Controllers\Auth\ForgotPasswordController@showLinkRequestForm | web,guest |
| | POST | password/reset | | App\Http\Controllers\Auth\ResetPasswordController@reset | web,guest |
| | GET|HEAD | password/reset/{token} | | App\Http\Controllers\Auth\ResetPasswordController@showResetForm | web,guest |
| | GET|HEAD | register | register | App\Http\Controllers\Auth\RegisterController@showRegistrationForm | web,guest |
| | POST | register | | App\Http\Controllers\Auth\RegisterController@register | web,guest |
+--------+----------+------------------------+----------+------------------------------------------------------------------------+--------------+

安装redis扩展

composer require predis/predis

修改配置文件

配置一下数据库(根目录下的.env文件)

DB_CONNECTION=mysql
DB_HOST=你的数据库IP
DB_PORT=3306
DB_DATABASE=你的数据库名
DB_USERNAME=你的用户名
DB_PASSWORD=你的密码 REDIS_HOST=192.168.1.100
REDIS_PASSWORD=null
REDIS_PORT=6379

配置好之后执行migrate命令

php artisan migrate

输出内容

Migration table created successfully.
Migrated: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_100000_create_password_resets_table

经过以上的操作准备工作就已经做好了

创建单用户中间件

接下来为了验证每次的url访问请求, 我们需要1个middleware

php artisan make:middleware SingleLoginMiddleware

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

<?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 ,修改后如下

<?php

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| This file is where you may define all of the routes that are handled
| by your application. Just tell Laravel the URIs it should respond
| to using a Closure or controller method. Build something great!
|
*/ 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 删掉部分代码, 修改后如下

<?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 的一个登录后方法, 以实现我们登录之后的一些处理.

<?php

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
这样就完成简单的单用户登录功能.

参考文档:
Laravel 单用户登录 作者:Destiny
PHP中的Traits详解 作者:tabalt

Laravel 5.3 单用户登录的简单实现的更多相关文章

  1. CentOS 单用户登录&命令行、图像界面

    如何单用户登录: 这是一个很简单的问题,以前没重视,每次linux服务器无法正常启动时,都找应急盘,想偷懒,反而浪费了时间. 今天备忘如下: 1.系统启动时,按光标键调出GRUB引导菜单. 2.选定一 ...

  2. Django实现单用户登录

    最近由于要毕业了写论文做毕设,然后还在实习发现已经好久都没有写博客了.今天由于工作需求,需要用Django实现单用户登录.大概意思就是跟QQ一样的效果,每个账号只能一个地方登录使用,限制账号的登录次数 ...

  3. 用redis和cookie做单用户登录

    因为公司的项目需要用到单用户登录,于是今天用redis和cookie给系统添加了单用户登录功能,再次简单记录一下. 单用户登录是为了防止同一账户在不同电脑和不同浏览器里面同时登录.所以我这边的思路是: ...

  4. salesforce 零基础学习(五十一)使用 Salesforce.com SOAP API 实现用户登录以及简单的增删改查(JAVA访问salesforce)

    此篇请参看:https://resources.docs.salesforce.com/202/latest/en-us/sfdc/pdf/salesforce_developer_environme ...

  5. aps .net MVC单用户登录

    当不允许多用户同时登录一个帐号时,就需要一种机制,当再登录一个相同的帐号时,前面登录的人被挤下线. 原文地址:http://www.cnblogs.com/f23wangj/p/4984302.htm ...

  6. Asp.NET WebApi+Redis实现单用户登录实战演练

    一.课程介绍 本次分享课程属于<C#高级编程实战技能开发宝典课程系列>中的一部分,阿笨后续会计划将实际项目中的一些比较实用的关于C#高级编程的技巧分享出来给大家进行学习,不断的收集.整理和 ...

  7. win2003设置单用户登录

    远程桌面是windows操作系统中一个很方便的功能,管理测试机资产.异地排除故障等,都很快捷.在windows xp sp2模式下,一般默认是单用户登录,也就是当A用户远程一台机器时,B用户在远程到这 ...

  8. ASP.NET MVC实现一个用户只能登录一次 单用户登录

    现在许多网站都要求登录后才能进行进一步的操作,当不允许多用户同时登录一个帐号时,就需要一种机制,当再登录一个相同的帐号时,前面登录的人被挤下线,或者禁止后面的人登录.这里实现的是前一种功能. 网上有许 ...

  9. centos7 设置grub密码及单用户登录实例

    centos7与centos6在设置grub密码的操作步骤上有很大的差别,特此记录供以后查用 grub加密的目的: 防止不法分子利用单用户模式修改root密码 给grub加密可以采用明文或者加密的密文 ...

随机推荐

  1. 《我的嵌入式开发》---- IIC 通信

    IIC 通用文件,文件是在NRF51xx 芯片基础,keil 平台开发测试通过,后期修改为STM32F2xx系列的配置. 文件百度云盘链接 : https://pan.baidu.com/s/1AFx ...

  2. tomcat配置接口访问时间

    这次刚好用到,亲测可用.参照:https://www.cnblogs.com/wuxun1997/p/9068398.html 项目中有些页面时延不稳定,需要看每次接口调用时延,怎么看,有两种方法:一 ...

  3. CentOS7.5实践快速部署LAMP+Tomcat成功运行阿里云或者腾讯云

    安装一定要按照顺序来 1 先安装JDK+TOMCAT 点击看这里 2 在安装LAMP  点击看这里 3 最关键的就是这里 LAMP+Tomcat整合 我们不用源码编译安装,而是使用yum命令来完成. ...

  4. LAB1 partII

    PartII   实现单词统计 实现 main/wc.go 两个函数 mapF() . reduceF() 单词是任意字母连续序列, 由unicode.IsLetter 决定字母 测试数据 pg-*. ...

  5. C# 6.0:Auto-Property initializer

    在之前的开发中,属性只能在构造函数中进行初始化,如果它有定义一个后台字段的话,那这个字段就就可以在定义的地方初始化.C# 6.0 引进了一个Auto-Property initializer机制使属性 ...

  6. Redis配置不当致使root被提权漏洞

    Redis配置不当致使root被提权漏洞 Dear all~ 最近Redis服务被曝出因配置不当,可能造成数据库被恶意清空,或被黑客利用写入后门文件造成进一步入侵,请关注! 一.漏洞发布日期 2015 ...

  7. TensorFlow-GPU+cuda8+cudnn6+anaconda安装遇到的版本错误

    第一遍装的时候是cuda10+cudnn5.1这个诡异的组合,失败 卸载cuda就是把所有的NVIDIA有关的应用都删掉,c盘文件也都删掉,不用留. 第二遍是cuda8+cudnn5.1.版本还是对不 ...

  8. linux杂记

    find [path] -name fileName find /home/audio/mrcp-asr/audio -type f -newermt '2019-04-23 00:00' ! -ne ...

  9. CBSN NEWS

    https://www.cbsnews.com/video/fatal-crossing/

  10. C#使用AppDomain时的异常分析:Object ‘XXXX.rem’ has been disconnected or does not exist at the server.

    在使用C#的应用程序域的时候,碰到这么一个异常: System.Runtime.Remoting.RemotingException: Object ‘/76e7cd41_2cd2_4e89_9c03 ...