【问题标题】:Authentication with 2 different tables使用 2 个不同的表进行身份验证
【发布时间】:2013-09-18 02:35:53
【问题描述】:

我需要用另一个表和用户创建一个新的“auth”配置。我有一个“管理员”用户的表和普通用户的另一个表。

但是如何使用不同的配置创建另一个 Auth 实例?

【问题讨论】:

  • 为什么需要对两个表进行身份验证?将每个人都放在同一个表中,并为管理员用户创建第二个包含特定字段的表。用外键连接这两个。这是最简单的方法。
  • 因为数据库已经在工作了。是这样设计的。我正在将代码移植到 Laravel。也许我应该手动做?
  • 我强烈建议您手动进行。不会有很多工作。您会发现这比在两个表上进行身份验证更容易。
  • 在完美的世界中,一个用户表是最好的,但这并不总是可行的。如果您可以将所有数据移动到一张表中,那就太好了。但是,如果你不能,我用 Eloquent 模型在两个表上进行了不到一个小时的身份验证。看看我下面的答案。
  • 就我而言,我需要能够在两个不同的表中检查登录凭据,因为我同时为员工和公司登录,并且他们在我的网站上具有完全不同的属性和功能。因此,有时您需要拥有多个用户表。

标签: authentication laravel-4


【解决方案1】:

在尝试自己解决这个问题时,我发现了一个更简单的方法。我基本上创建了一个自定义 ServiceProvider 来替换默认的 Auth 一个,它充当 Auth 的工厂类,并允许您为多种登录类型拥有多个实例。我还把它全部放在一个可以在这里找到的包中:https://github.com/ollieread/multiauth

它真的很容易使用,只需将 app/config/app.php 中的 AuthServiceProvider 替换为 Ollieread\Multiauth\MultiauthServiceProvider,然后将 app/config/auth.php 更改为如下所示:

return array(

    'multi' => array(
        'account' => array(
            'driver' => 'eloquent',
            'model' => 'Account'
        ),
        'user' => array(
            'driver' => 'database',
            'table' => 'users'
        )
    ),

    'reminder' => array(

        'email' => 'emails.auth.reminder',

        'table' => 'password_reminders',

        'expire' => 60,

    ),

);

现在您可以像以前一样使用 Auth,但有一点不同:

Auth::account()->attempt(array(
    'email'     => $attributes['email'],
    'password'  => $attributes['password'],
));
Auth::user()->attempt(array(
    'email'     => $attributes['email'],
    'password'  => $attributes['password'],
));
Auth::account()->check();
Auth::user()->check();

它还允许您同时以多个用户类型登录,这是我正在从事的项目的要求。希望它对我以外的人有所帮助。

更新 - 27/02/2014

对于那些刚刚遇到这个答案的人,我最近添加了对提醒的支持,可以以相同的工厂样式方式访问。

【讨论】:

    【解决方案2】:

    您可以“模拟”一个新的 Auth 类。

    Laravel Auth 组件基本上就是Illuminate\Auth\Guard 类,这个类有一些依赖。

    所以,基本上你必须创建一个新的 Guard 类和一些门面......

    <?php 
    use Illuminate\Auth\Guard as AuthGuard;
    
    class CilentGuard extends AuthGuard
    {
    
        public function getName()
        {
            return 'login_' . md5('ClientAuth');
        }
    
        public function getRecallerName()
        {
            return 'remember_' . md5('ClientAuth');
        }
    }
    

    ...添加一个ServiceProvider 来初始化这个类,传递它的依赖关系。

    <?php 
    
    use Illuminate\Support\ServiceProvider;
    use Illuminate\Auth\EloquentUserProvider;
    use Illuminate\Hashing\BcryptHasher;
    use Illuminate\Auth\Reminders\PasswordBroker;
    use Illuminate\Auth\Reminders\DatabaseReminderRepository;
    use ClientGuard;
    use ClientAuth;
    
    class ClientServiceProvider extends ServiceProvider 
    {
    
        public function register()
        {
            $this->registerAuth();
            $this->registerReminders();
        }
    
        protected function registerAuth()
        {
            $this->registerClientCrypt();
            $this->registerClientProvider();
            $this->registerClientGuard();
        }
    
        protected function registerClientCrypt()
        {
            $this->app['client.auth.crypt'] = $this->app->share(function($app)
            {
                return new BcryptHasher;
            });
        }
    
        protected function registerClientProvider()
        {
            $this->app['client.auth.provider'] = $this->app->share(function($app)
            {
                return new EloquentUserProvider(
                    $app['client.auth.crypt'], 
                    'Client'
                );
            });
        }
    
        protected function registerClientGuard()
        {
            $this->app['client.auth'] = $this->app->share(function($app)
            {
                $guard = new Guard(
                    $app['client.auth.provider'], 
                    $app['session.store']
                );
    
                $guard->setCookieJar($app['cookie']);
                return $guard;
            });
        }
    
        protected function registerReminders()
        {
            # DatabaseReminderRepository
            $this->registerReminderDatabaseRepository();
    
            # PasswordBroker
            $this->app['client.reminder'] = $this->app->share(function($app)
            {
                return new PasswordBroker(
                    $app['client.reminder.repository'], 
                    $app['client.auth.provider'], 
                    $app['redirect'], 
                    $app['mailer'], 
                    'emails.client.reminder' // email template for the reminder
                );
            });
        }
    
        protected function registerReminderDatabaseRepository()
        {
            $this->app['client.reminder.repository'] = $this->app->share(function($app)
            {
                $connection   = $app['db']->connection();
                $table        = 'client_reminders';
                $key          = $app['config']['app.key'];
    
                return new DatabaseReminderRepository($connection, $table, $key);
            });
        }
    
        public function provides()
        {
            return array(
                'client.auth', 
                'client.auth.provider', 
                'client.auth.crypt', 
                'client.reminder.repository', 
                'client.reminder', 
            );
        }
    }
    

    在这个服务提供者中,我举了一些例子来说明如何创建一个“新”密码提醒组件。

    现在您需要创建两个新外观,一个用于身份验证,一个用于密码提醒。

    <?php 
    use Illuminate\Support\Facades\Facade;
    
    class ClientAuth extends Facade
    {
    
        protected static function getFacadeAccessor() 
        {
            return 'client.auth';
        }
    }
    

    还有……

    <?php 
    use Illuminate\Support\Facades\Facade;
    
    class ClientPassword extends Facade
    {
    
        protected static function getFacadeAccessor() 
        {
            return 'client.reminder';
        }
    }
    

    当然,对于密码提醒,您需要在数据库中创建表才能工作。在这个例子中,表名应该是client_reminders,你可以在Service Provider的registerReminderDatabaseRepository方法中看到。表结构与原始提醒表相同。

    之后,您可以像使用 Auth 类一样使用 ClientAuth。对于 ClientPasswordPassword 类也是如此。

    ClientAuth::gust();
    ClientAuth::attempt(array('email' => $email, 'password' => $password));
    
    ClientPassword::remind($credentials);
    

    不要忘记将您的服务提供商添加到app/config/app.php 文件中的服务提供商列表中。

    更新:

    如果您使用的是 Laravel 4.1,PasswordBroker 不再需要 Redirect 类。

    return new PasswordBroker(
        $app['client.reminder.repository'], 
        $app['client.auth.provider'], 
        $app['mailer'], 
        'emails.client.reminder' // email template for the reminder
    );
    

    更新 2

    Laravel 5.2 刚刚引入了multi auth,所以这个版本不再需要了。

    【讨论】:

    • 在这个例子中,我“假设”你有一个客户端模型,你应该用你的真实模型替换。另外我建议使用一些命名空间来组织这些类。
    • 这个答案不完整,并且有几个错误。您还需要确保将 ClientServiceProvider 添加到 config/app.php 中的提供程序列表中,然后运行 ​​php artisan dump-autoload 否则它将不起作用。在 ServiceProvider 的 registerClientGuard() 函数中,$guard = new Guard 的行应该是 $guard = new ClientGuard。在最后一个代码块中,说明 ClientAuth::gust() 的行应该是 ClientAuth::guest()。我尝试将其作为编辑提交,因为上面的代码无法按原样运行,但由于某种原因被拒绝。
    • 另外,如果你运行的是 Laravel 4.0.9 或更新版本,你需要删除 boot 函数以及 registerAuthEvents() 函数的内容。原因是 getQueuedCookies() 在 4.0.9 中被移除了,所以会导致错误。
    • @JordanPatterson 你能解释一下如何解决getQueuedCookies 错误吗?
    • @user1692333 删除它。不再需要它。
    【解决方案3】:

    好的,我遇到了同样的问题,我是这样解决的:

    实际上,在 laravel 4 中,您可以简单地在运行时更改身份验证配置,因此您可以在 App::before 过滤器中简单地执行以下操作:

    if ($request->is('admin*'))
    {
        Config::set('auth.model', 'Admin');
    }
    

    这将使 Auth 组件在管理 url 中使用管理模型。但这将导致一个新问题,因为如果您的 admins 和 users 表中有两个用户具有相同的 id,那么登录会话密钥是相同的,如果您之前以普通用户!所以为了使两个不同的身份验证完全独立,我做了这个技巧:

    class AdminGuard extends Guard
    {
        public function getName()
        {
            return 'admin_login_'.md5(get_class($this));
        }
    
        public function getRecallerName()
        {
            return 'admin_remember_'.md5(get_class($this));
        }
    }
    
    Auth::extend('eloquent.admin', function()
    {
        return new AdminGuard(new EloquentUserProvider(new BcryptHasher, 'Admin'), App::make('session.store'));
    });
    

    并将 App::before 代码更改为:

    if ($request->is('admin*'))
    {
        Config::set('auth.driver', 'eloquent.admin');
        Config::set('auth.model', 'Admin');
    }
    

    您可以看到我创建了一个新的身份验证驱动程序并重写了 Guard 类的一些方法,因此它将为管理站点生成不同的会话密钥。然后我更改了管理站点的驱动程序。祝你好运。

    【讨论】:

    • 如果这种基于路由/请求的方法适合您,我会很感兴趣。这听起来像是我可以尝试的东西。在基本使用层面上,我不明白为什么这样的东西不起作用......
    • 您可以根据需要更改驱动程序。
    • @Amir , 你把 AdminGuard 类放在哪里了?
    • @nabill。你可以把它放在任何你想要的地方,因为你可以用 composer autoload 来加载它,但是因为这是我做的唯一扩展,所以我把它放在 global.php 文件中。
    • @Amir 我在哪里可以添加 adminGuard 类。我必须将 AdminGuard 类放在哪个文件夹中?
    【解决方案4】:

    我昨天遇到了同样的问题,最后我创建了一个更简单的解决方案。

    我的要求是在两个不同的数据库中有 2 个不同的表。一张桌子供管理员使用,另一张桌子供普通用户使用。此外,每个表都有自己的散列方式。我最终得到了以下内容(代码也可作为 Github 上的要点:https://gist.github.com/Xethron/6790029

    创建一个新的用户提供者。我叫我的 MultiUserProvider.php

    <?php
    
    // app/libraries/MultiUserProvider.php
    
    use Illuminate\Auth\UserProviderInterface,
        Illuminate\Auth\UserInterface,
        Illuminate\Auth\GenericUser;
    
    class MultiUserProvider implements UserProviderInterface {
    
      protected $providers;
    
        public function __construct() {
    
            // This should be moved to the config later...
            // This is a list of providers that can be used, including
            // their user model, hasher class, and hasher options...
            $this->providers = array(
                'joomla' => array(
                    'model' => 'JoomlaUser',
                    'hasher' => 'JoomlaHasher',
                    )
                'another' => array(
                    'model' => 'AnotherUser',
                    'hasher' => 'AnotherHasher',
                    'options' => array(
                        'username' => 'empolyee_number',
                        'salt' => 'salt',
                        )
                    ),
                );
        }
        /**
         * Retrieve a user by their unique identifier.
         *
         * @param  mixed  $identifier
         * @return \Illuminate\Auth\UserInterface|null
         */
        public function retrieveById($identifier)
        {
            // Returns the current provider from the session.
            // Should throw an error if there is none...
            $provider = Session::get('user.provider');
    
            $user = $this->createModel($this->providers[$provider]['model'])->newQuery()->find($identifier);
    
            if ($user){
                $user->provider = $provider;
            }
    
            return $user;
        }
    
        /**
         * Retrieve a user by the given credentials.
         *
         * @param  array  $credentials
         * @return \Illuminate\Auth\UserInterface|null
         */
        public function retrieveByCredentials(array $credentials)
        {
            // First we will add each credential element to the query as a where clause.
            // Then we can execute the query and, if we found a user, return it in a
            // Eloquent User "model" that will be utilized by the Guard instances.
    
            // Retrieve the provider from the $credentials array.
            // Should throw an error if there is none...
            $provider = $credentials['provider'];
    
            $query = $this->createModel($this->providers[$provider]['model'])->newQuery();
    
            foreach ($credentials as $key => $value)
            {
                if ( ! str_contains($key, 'password') && ! str_contains($key, 'provider'))
                    $query->where($key, $value);
            }
    
            $user = $query->first();
    
            if ($user){
                Session::put('user.provider', $provider);
                $user->provider = $provider;
            }
    
            return $user;
        }
    
        /**
         * Validate a user against the given credentials.
         *
         * @param  \Illuminate\Auth\UserInterface  $user
         * @param  array  $credentials
         * @return bool
         */
        public function validateCredentials(UserInterface $user, array $credentials)
        {
            $plain = $credentials['password'];
    
            // Retrieve the provider from the $credentials array.
            // Should throw an error if there is none...
            $provider = $credentials['provider'];
    
            $options = array();
    
            if (isset($this->providers[$provider]['options'])){
                foreach ($this->providers[$provider]['options'] as $key => $value) {
                    $options[$key] = $user->$value;
                }
            }
    
            return $this->createModel($this->providers[$provider]['hasher'])
                ->check($plain, $user->getAuthPassword(), $options);
        }
    
        /**
         * Create a new instance of a class.
         *
         * @param string $name Name of the class
         * @return Class
         */
        public function createModel($name)
        {
            $class = '\\'.ltrim($name, '\\');
    
            return new $class;
        }
    
    }
    

    然后,我通过在我的 app/start/global.php 文件顶部添加以下行来告诉 Laravel 我的 UserProvider。

    // app/start/global.php
    
    // Add the following few lines to your global.php file
    Auth::extend('multi', function($app) {
        $provider =  new \MultiUserProvider();
    
        return new \Illuminate\Auth\Guard($provider, $app['session']);
    });
    

    然后,我告诉 Laravel 在 app/config/auth.php 中使用我的用户提供程序而不是 EloquentUserProvider

    'driver' => 'multi',
    

    现在,当我进行身份验证时,我会这样做:

    Auth::attempt(array(
        'email' => $email,
        'password' => $password,
        'provider'=>'joomla'
        )
    )
    

    然后该类将使用带有 joomlaHasher 的 joomlaUser 模型,并且没有用于哈希器的选项...如果使用“另一个”提供程序,它将包括用于哈希器的选项。

    这个类是为我的需要而构建的,但可以很容易地改变以适应你的需要。

    PS:确保自动加载器可以找到 MultiUserProvider,否则它将无法工作。

    【讨论】:

    • 我要试试这个,但将新提供者放在 'providers' => config/app.php 上的数组中并不是更好
    • @CarlosGoce:是的,我还没有这样做,不过,我想 config/auth.php 会是一个更好的放置位置...
    • 如果您确实进行了所需的更改,如果您能在我的要点上添加拉取请求并提供任何更新,我将不胜感激:)
    • Laravel (4.0.7) 的最新更新将 $app['session'] 更改为 $app['session.store']
    • 使用这种方法,Laravel 是否允许我同时以 'joomla' 和 'another' 用户身份登录?
    【解决方案5】:

    我正在使用 Laravel 5 原生身份验证来处理多个用户表...

    这并不难,请检查这个要点:

    https://gist.github.com/danielcoimbra/64b779b4d9e522bc3373

    更新:对于 Laravel 5,如果您需要更强大的解决方案,请试试这个包:

    https://github.com/sboo/multiauth

    丹尼尔

    【讨论】:

      猜你喜欢
      • 2021-10-06
      • 1970-01-01
      • 1970-01-01
      • 2013-06-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-18
      相关资源
      最近更新 更多