Laravel 5.2 或 5.3:如何正确实施检查会话是不是已登录

Posted

技术标签:

【中文标题】Laravel 5.2 或 5.3:如何正确实施检查会话是不是已登录【英文标题】:Laravel 5.2 or 5.3: How to Properly Implement a check to see if a session is Logged in alreadyLaravel 5.2 或 5.3:如何正确实施检查会话是否已登录 【发布时间】:2017-01-31 07:53:36 【问题描述】:

我正在使用 Laravel 5.2.45 开发一个应用程序,该应用程序与离子前端交互,并与使用 Postman 测试的服务器端交互。对于如何正确实施检查以查看用户的会话是否已经处于活动状态并已登录,我有点困惑。下面是一个概述的场景:

删除本地存储: 假设用户向系统注册并登录。在离子浏览器和邮递员中进行测试时,所有适当的信息都会返回。此外,会话也在数据库中生成,而不是文件(如预期的那样,不,我们不想将文件用于会话)。 现在假设用户检查浏览器并删除其本地存储数据并删除所有缓存的信息和 cookie。删除所有客户端存储的数据后,用户尝试使用相同的凭据登录。这就是问题发生的地方。

问题: 当用户点击 /login 路由时,这将自动在数据库中生成另一个新会话,而无需首先检查数据库中是否已经存在另一个活动会话。每次我 ping /login 路由时都会发生这种情况。所以本质上,如果有人登录,然后删除他们的本地存储并再次登录(并登录 1000 多次),他们最终可能会在会话表中用错误的会话淹没数据库,所有这些都附加到用户的帐户。

只有当他们从 2 个不同的设备登录时,才会在会话表中拥有多个会话。但在这种情况下,当时应该只有 1 个会话处于活动状态,但数据库中仍会为该用户记录 2 个会话。

问题: 如何或在哪里 我会为系统实施干净、正确的方法来通过中间件检查数据库中是否已经存在实时/活动会话 和 根据数据库中的活动会话,对用户尝试重新登录的凭据进行身份验证/附加到该活动会话?

此问题基于全新安装 Laravel (5.2.45) 或更高版本。没有向服务器端添加额外代码,也没有使用刀片模板。重定向是通过 ionic 平台进行的,在 Postman 中进行测试时应该只返回数据。

我去了哪里:

下面的链接准确但描述性不够,说明如何实现它并进一步发展,因为在它执行 Auth::check() 之后我还有其他流程需要发生 how to check if user is logged in by his session in route and then call controller method in laravel?

非常感谢您在这方面的任何帮助。 谢谢!

【问题讨论】:

【参考方案1】:

我做以下事情:

1) 在routes.php 中我定义了路由组的中间件:

Route::group(['prefix' => 'auth'], function() 
  Route::get('/', ['as' => 'auth', 'uses' => 'AuthController@index']);
  Route::post('/', ['as' => 'auth.attempt', 'uses' => 'AuthController@attempt']);
  Route::delete('/', ['uses' => 'AuthController@destroy']);
  Route::any('destroy', ['as' => 'auth.destroy', 'uses' => 'AuthController@destroy']);
);

Route::group(['prefix' => 'billing', 'namespace' => 'Billing', 'middleware' => ['App\Http\Middleware\HasAccessToBilling']], function()

    Route::any('/', ['as' => 'billing', 'uses' => 'DashboardController@index']);
    Route::get('profile', ['as' => 'billing.profile', 'uses' => 'ProfileController@index']);
);

2) 在app/Http/Middleware/HasAccessToBilling.php 我定义:

<?php namespace App\Http\Middleware;

use App\Library\Auth;
use Closure;
use Illuminate\Http\Request;

class HasAccessToBilling


    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request $request
     * @param  \Closure $next
     * @return mixed
     */
    public function handle(Request $request, Closure $next)
    
        if (Auth::hasAccessTo('billing', $request)) 
            return $next($request);
        
        return redirect()->route('auth');
    

3) 在app/Library/Auth.php : (自定义Auth lib)

<?php namespace App\Library;

use \App\Models\User;  // I keep Models in app/Models folder and define namespace App\Models;
use Illuminate\Http\Request;
use Crypt;

class Auth

    const REALMS = 'api,billing';

    public static function attempt($realm, Request $request)
    
        $username = $request->input('username');
        $password = $request->input('password');
        $remember = $request->input('remember', false);

        $User = (filter_var($username, FILTER_VALIDATE_EMAIL)) ?
            User::whereEmail($username)->first()
            : User::whereUsername($username)->first();
        if (!$User) 
            return false;
        

        if (!$User->checkPassword($password)) 
            return false;
        

        $realms = (is_array($realm) AND !empty($realm)) ? $realm : [$realm];
        $auth = [
            'timestamp' => time(),
            'user_id' => $User->id,
            'access_to' => [],
            'roles' => [],
            'permissions' => []
        ];
        $auth = $request->session()->get('auth', $auth);
        foreach ($realms AS $realm) 
            if (!in_array($realm, $auth['access_to'])) 
                $auth['access_to'][] = $realm;
            
        

        if($remember) 
            $rememberToken = Crypt::encrypt(json_encode($auth));
            $auth['remember-token'] = $rememberToken;
        
        $request->session()->put('auth', $auth);

        return $auth;
    

    public static function destroy(Request $request, $realm = null)
    
        if (is_null($realm)) 
            $request->session()->forget('auth');
            return true;
        

        $auth = $request->session()->get('auth');
        if (isset($auth['access_to'])) 
            $realms = (is_array($realm) AND !empty($realm)) ? $realm : [$realm];
            foreach ($realms AS $realm) 
                $key = array_search($realm, $auth['access_to']);
                unset($auth['access_to'][$key]);
            
            $auth['access_to'] = array_values($auth['access_to']);
            if(sizeof($auth['access_to']) > 0) 
                $request->session()->put('auth', $auth);
            
            else 
                $request->session()->forget('auth');
            
            return true;
        
        return false;
    

    public static function recoverSession(Request $request)
    
        $rememberToken = $request->cookie('remember-token', null);
        if(is_null($rememberToken)) 
            return null;
        

        try
            $rememberToken = Crypt::decrypt($rememberToken);
            $auth = json_decode($rememberToken, true);
            $request->session()->set('auth', $auth);
        
        catch(\Exception $ex) 

        return $request->session()->get('auth');
    

    public static function hasAccessTo($realm, Request $request)
    
        $auth = $request->session()->get('auth', null);
        if (is_null($auth)) 
            $auth = self::recoverSession($request);
        

        return (isset($auth['access_to']))?
                in_array($realm, $auth['access_to'])
                : false;
    

4) 在app/Models/User.php: (别忘了创建 Models 文件夹)

<?php namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Hash;
use Closure;

class User extends Model

    const USERNAME_MAXLEN = 2;
    const PASSWORD_MAXLEN = 5;
    protected $table = 'users';
    protected $fillable = ['username', 'email', 'password', 'active', 'deleted'];

    public function checkPassword($password)
    
        return Hash::check($password, $this->password);
    

    public function updateAndCall($attributes = [], Closure $closure) 
        if(isset($attributes['password'])) 
            $attributes['password'] = Hash::make($attributes['password']);
        
        $this->update($attributes);
        return $closure($this);
    

    public static function createAndCall($attributes = [], Closure $closure) 
        if(isset($attributes['password'])) 
            $attributes['password'] = Hash::make($attributes['password']);
        
        $Record = self::create($attributes);
        return $closure($Record);
    

5) 在app\Http\Controllers\AuthController.php

<?php namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Library\Auth;

class AuthController extends Controller

    public function index()
    

        return view('auth.index');
    

    public function attempt(Request $request)
    
        $realms = $request->get('realm', Auth::REALMS);
        if (!is_null($realms)) 
            $realms = explode(',', $realms);
        

        $auth = Auth::attempt($realms, $request);
        if ($auth === false) 
            return $this->forbidden('Username and/or Password invalid!');
        

        foreach ($realms AS $realm) 
            if (!Auth::hasAccessTo($realm, $request)) 
                return $this->forbidden('Access denied');
            
        

        if (isset($auth['remember-token'])) 
            $this->setCookie('remember-token', $auth['remember-token'], 525600); // 1 year
        

        return $this->ok(null, ['redirectTo' => $realms[0]]);
    

    public function destroy(Request $request)
    
        $realms = $request->get('realm', Auth::REALMS);
        if (is_array($realms) AND !empty($realms)) 
            $realms = explode(',', $realms);
        
        Auth::destroy($request, $realms);
        $this->deleteCookie('remember-token');

        return redirect()->route('auth');
    

随意填写,随心所欲地使用它(;

【讨论】:

感谢您的快速响应。如果一切顺利,我会调查你的概念并申请。如果我最终这样做,我会标记为已回答!再次感谢您对此的帮助。 您提供的示例中显示的概念帮助我解决了问题的根本原因以及如何正确实施会话处理。当我稳定一切后,我会在适当的时候发布我得到的结果,希望能帮助其他可能遇到我发现的相同问题的人。再次感谢伙计! @user3124770 我真的希望这是最适合您的解决方案。

以上是关于Laravel 5.2 或 5.3:如何正确实施检查会话是不是已登录的主要内容,如果未能解决你的问题,请参考以下文章

在升级laravel 5.2到5.3之后,无法在Route :: model中绑定模型

Laravel 5.3 用户验证源码探究 路由与注册

如何正确删除 Laravel 5.3 中用户表列中的“唯一”列属性?

Laravel 5.3 保护下载文件的正确方法

Vue 2 Laravel 5.3 Vue-router 2 设置 Vue-router 的正确方法

如何在 Laravel 5.2 中手动发送密码重置请求?