應用場景
領導要求使用者同時只能在一個地方登陸,不能同時在兩個地點登陸,你反抗是沒有用的,實現吧騷年!
思路
- 1.在使用者表中增加一個
session_id
欄位。 - 2.使用者登陸時將當前的
session_id
儲存到資料庫中。 - 3.新增一箇中介軟體,來過濾已經登陸授權的使用者。過濾行為是這樣的:判斷當前的
session_id
是否和資料庫中的相同,如果相同,執行下一步。如果不同,退出當前使用者並給出提示。
實現過程
第一步:省略。
第二步:
這裡有個坑,請繞行:天(wu)真(zhi)的我以為只要在 LoginController
中重寫 login()
方法即可,其實我錯了。在 laravel 文件的 session 這一節中有 重新生成 Session ID 一段小內容:大致講,如果你使用了內建函式 LoginController
,laravel 會自動重新生成身份驗證中 Session ID。我們發現 login()
方法並沒有做 $request->session()->regenerate();
這個動作,那麼在這裡儲存 session_id
後必然不可行。往後看發現當判斷登陸成功後會執行這麼一個動作 return $this->sendLoginResponse($request);
去看看 sendLoginResponse($request)
這個方法:
vendor/laravel/framework/src/Illuminate/Foundation/Auth/AuthenticatesUsers.php
.
.
.
/**
* 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);
return $this->authenticated($request, $this->guard()->user())
?: redirect()->intended($this->redirectPath());
}
.
.
.
果然是這裡,那麼我們在 LoginController
中重寫:
app/Http/Controllers/Auth/LoginController.php
.
.
.
/**
* 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();
// 儲存 session_id
$authUser=Auth::user();
$authUser->session_id = $request->session()->getId();
$authUser->save();
$this->clearLoginAttempts($request);
return $this->authenticated($request, $this->guard()->user())
?: redirect()->intended($this->redirectPath());
}
.
.
.
第三步:
建立中介軟體
$ php artisan make:middleware OnlyOnePlaceLogin
app/Http/Middleware/OnlyOnePlaceLogin.php
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
class OnlyOnePlaceLogin
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
if (Auth::check()) {
// 已儲存的 session_id
$storedSessionId = Auth::user()->session_id;
// 當前授權後重新生成的 session_id
$sessionId=$request->session()->getId();
if($sessionId!=$storedSessionId){
Auth::logout();
session()->flash('danger', '已在其地方登陸或會話已過期,請重新登陸。');
return redirect('login');
}
}
return $next($request);
}
}
註冊中介軟體,為路由分配中介軟體:'oopl' => \App\Http\Middleware\OnlyOnePlaceLogin::class,
。
app/Http/Kernel.php
.
.
.
/**
* The application's route middleware.
*
* These middleware may be assigned to groups or used individually.
*
* @var array
*/
protected $routeMiddleware = [
'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'oopl' => \App\Http\Middleware\OnlyOnePlaceLogin::class,
];
.
.
.
在 路由
或者 控制器
中使用中介軟體
app/Http/Controllers/ProjectController.php
.
.
.
public function __construct()
{
$this->middleware('oopl');
}
.
.
.
效果
這裡不講效能和使用者體驗,只講功能實現,如有錯誤,請批評改正。