教程:已在其地方登陸或會話已過期,請重新登陸

mayingbiao89發表於2018-05-30

應用場景

領導要求使用者同時只能在一個地方登陸,不能同時在兩個地點登陸,你反抗是沒有用的,實現吧騷年!

思路

  • 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');
}
.
.
.

效果

file

這裡不講效能和使用者體驗,只講功能實現,如有錯誤,請批評改正。

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章