无全局作用域的Laravel身份验证

无全局作用域的Laravel身份验证,laravel,authentication,Laravel,Authentication,在my Laravel应用程序中,用户可以禁用(而不是删除)他们的帐户以从网站上消失。但是,如果他们再次尝试登录,他们的帐户应自动激活,并应成功登录 这是通过用户表中的“活动”列和用户模型中的全局范围完成的: protected static function boot() { parent::boot(); static::addGlobalScope('active', function(Builder $builder) { $builder->wh

在my Laravel应用程序中,用户可以禁用(而不是删除)他们的帐户以从网站上消失。但是,如果他们再次尝试登录,他们的帐户应自动激活,并应成功登录

这是通过用户表中的“活动”列和用户模型中的全局范围完成的:

protected static function boot() {
    parent::boot();

    static::addGlobalScope('active', function(Builder $builder) {
        $builder->where('active', 1);
    });
}
现在的问题是,这些非活动帐户无法再次登录,因为AuthController找不到它们(超出范围)

我需要达到的目标是:

  • 使AuthController忽略全局范围“活动”
  • 如果用户名和密码正确,则将“活动”列值更改为“1”
  • 我现在的想法是使用Without GlobalScope定位用户,手动验证密码,将列“active”更改为1,然后继续常规登录

    在postLogin方法中的my AuthController中:

    $user = User::withoutGlobalScope('active')
                ->where('username', $request->username)
                ->first();
    
    if($user != null) {
        if (Hash::check($request->username, $user->password))
        {
            // Set active column to 1
        }
    }
    
    return $this->login($request);
    
    所以问题是如何让AuthController忽略全局作用域而不改变Laravel主代码,这样它将保留更新


    谢谢。

    创建一个类
    GlobalUserProvider
    ,该类扩展了
    EloquentUserProvider
    ,如下所示

    class GlobalUserProvider extends EloquentUserProvider {
    
        public function createModel() {
             $model = parent::createModel();
             return $model->withoutGlobalScope('active');
        }
    
    }
    
    AuthServiceProvider
    中注册您的新用户提供程序:

    Auth::provider('globalUserProvider', function ($app, array $config) {
         return new GlobalUserProvider($this->app->make('hash'), $config['model']);
    });
    
    最后,您应该在
    auth.php
    config文件中将用户提供程序驱动程序更改为globalUserProvider

     'providers' => [
        'users' => [
            'driver' => 'globalUserProvider',
            'model' => App\Models\User::class
        ]
     ]
    

    请尝试此登录问题,您可以在登录后使用
    而不使用GlobalScopes()

    使用您在OP中使用的代码扩展
    AuthController
    。这应该可以工作

    public function postLogin(Request $request)
    {
        $user = User::withoutGlobalScope('active')
                    ->where('username', $request->username)
                    ->first();
    
        if($user != null){
            if (Hash::check($request->password, $user->password)){
                $user->active = 1;
                $user->save();
            }
        }
    
        return $this->login($request);
    }
    

    @Sasan的答案在Laravel 5.3中运行良好,但在5.4中不起作用-createModel()需要一个
    Model
    ,但得到一个
    Builder
    对象,因此当
    EloquentUserProvider
    调用
    $Model->getAuthIdentifierName()
    时会引发异常:

    BadMethodCallException: Call to undefined method Illuminate\Database\Query\Builder::getAuthIdentifierName() in /var/www/vendor/laravel/framework/src/Illuminate/Database/Query/Builder.php:2445
    
    相反,请遵循相同的方法,但覆盖更多函数,以便从
    createModel()
    返回正确的对象

    getQuery()
    返回没有全局作用域的生成器,其他两个函数使用全局作用域

    class GlobalUserProvider extends EloquentUserProvider
    {
        /**
         * Get query builder for the model
         * 
         * @return \Illuminate\Database\Eloquent\Builder
         */
        private function getQuery()
        {
            $model = $this->createModel();
    
            return $model->withoutGlobalScope('active');
        }
    
        /**
         * Retrieve a user by their unique identifier.
         *
         * @param  mixed  $identifier
         * @return \Illuminate\Contracts\Auth\Authenticatable|null
         */
        public function retrieveById($identifier)
        {
            $model = $this->createModel();
    
            return $this->getQuery()
                ->where($model->getAuthIdentifierName(), $identifier)
                ->first();
        }
    
        /**
         * Retrieve a user by their unique identifier and "remember me" token.
         *
         * @param  mixed  $identifier
         * @param  string  $token
         * @return \Illuminate\Contracts\Auth\Authenticatable|null
         */
        public function retrieveByToken($identifier, $token)
        {
            $model = $this->createModel();
    
            return $this->getQuery()
                ->where($model->getAuthIdentifierName(), $identifier)
                ->where($model->getRememberTokenName(), $token)
                ->first();
        }
    }
    

    我通过创建新包解决了这个问题

    运行
    composer require-mpyw/scoped-auth
    并修改您的用户模型,如下所示:

    <?php
    
    namespace App;
    
    use Illuminate\Auth\Authenticatable;
    use Illuminate\Contracts\Auth\Authenticatable as UserContract;
    use Illuminate\Database\Eloquent\Builder;
    use Illuminate\Database\Eloquent\Model;
    use Mpyw\ScopedAuth\AuthScopable;
    
    class User extends Model implements UserContract, AuthScopable
    {
        use Authenticatable;
    
        public function scopeForAuthentication(Builder $query): Builder
        {
            return $query->withoutGlobalScope('active');
        }
    }
    
     


    萨桑·法罗克有一个正确的答案。唯一不重写createModel而重写newModelQuery的方法就是这样做

    protected function newModelQuery($model = null)
    {
        $modelQuery = parent::newModelQuery();
        return $modelQuery->withoutGlobalScope('active');
    }
    
    我不得不使用
    ->不使用GlobalScopes()


    为了让它工作

    我遇到了类似的问题。我使用了这个解决方案:创建两个独立的用户模型。第一个没有全局作用域,仅用于身份验证。第二个扩展了第一个,包括全局范围,并用于其他所有内容。如果我错了,请纠正我,但您在这里没有使用不带全局范围的
    回答您自己的问题吗?请参阅链接:这不起作用<代码>$model->without globalscope('active')失败,因为父级::createModel()返回一个用户对象<但是,必须在查询中调用code>Without GlobalScope
    。对我来说非常有效-帮助避免了
    Symfony\Component\Debug\Exception\FatalThrowable错误:已达到最大函数嵌套级别“256”,正在中止全局作用域应用于用户模型时出错,该模型正在检查用户是否经过身份验证(从而再次引导用户)。对,您必须重写此函数,而不是newModelQuery()。这对以后版本的Laravel有效吗?我试图在文档中找到“postLogin”,但找不到。此外,AuthController现在似乎是LoginController和RegisterController。这里的问题是在
    auth
    中间件中,它将看不到任何auth用户,因此您将被记录out@TheGeeky我没有在这里使用auth中间件任何其他路由,“<代码> Auth<代码>中间件将使用您所做的范围,并考虑您的访客用户名不是真的-我正在设置$USER -> Active=1,这样,下一次他们提出请求时,它就认为是一个活动用户,并将与AuthMealDeWorksAudio.<代码> Auth中间件将自动注销用户,因此这是一个坏的解决方案。
    <?php
    
    namespace App\Providers;
    
    use Illuminate\Support\Facades\Event;
    use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
    
    class EventServiceProvider extends ServiceProvider
    {
        /**
         * The event listener mappings for the application.
         *
         * @var array
         */
        protected $listen = [
            \Illuminate\Auth\Events\Login::class => [
                \App\Listeners\ActivateUser::class,
            ],
        ];
    
        /**
         * Register any events for your application.
         *
         * @return void
         */
        public function boot()
        {
            parent::boot();
    
            //
        }
    }
    
    <?php
    
    namespace App\Listeners;
    
    use Illuminate\Auth\Events\Login;
    
    class ActivateUser
    {
        /**
         * Handle the event.
         *
         * @param  Illuminate\Auth\Events\Login $event
         * @return void
         */
        public function handle(Login $event)
        {
            $event->user->fill('active', 1)->save();
        }
    }
    
    protected function newModelQuery($model = null)
    {
        $modelQuery = parent::newModelQuery();
        return $modelQuery->withoutGlobalScope('active');
    }