Laravel 5.1多个身份验证

5

如何在Laravel 5.1中对多种类型的用户进行身份验证,例如求职者、招聘者、管理员等。

有些人建议仅使用单一用户表存储密码和电子邮件,创建配置文件表以存储用户特定信息(jobseeker_profile,recruiter_profile),并使用角色来区分不同类型的用户(即拥有roles和role_user表)。

这很好,但是如果不同类型的用户具有不同的注册和登录表单,该怎么办?如何自定义默认身份验证控制器以显示正确的视图?

因此,如果我有以下路由:

// Jobseeker Authentication routes...
Route::get('auth/login', 'Auth\AuthController@getLogin');
Route::post('auth/login', 'Auth\AuthController@postLogin');
Route::get('auth/logout', 'Auth\AuthController@getLogout');

// Jobseeker Registration routes...
Route::get('auth/register', 'Auth\AuthController@getRegister');
Route::post('auth/register', 'Auth\AuthController@postRegister');


// Recruiter Authentication routes...
Route::get('recruiter/auth/login', 'Auth\AuthController@getLogin');
Route::post('recruiter/auth/login', 'Auth\AuthController@postLogin');
Route::get('recruiter/auth/logout', 'Auth\AuthController@getLogout');

// Recruiter Registration routes...
Route::get('recruiter/auth/register', 'Auth\AuthController@getRegister');
Route::post('recruiter/auth/register', 'Auth\AuthController@postRegister');

这是开箱即用的默认认证控制器:

class AuthController extends Controller
{
    use AuthenticatesAndRegistersUsers;

    public function __construct()
    {
        $this->middleware('guest', ['except' => 'getLogout']);
    }

    protected function validator(array $data)
    {
        return Validator::make($data, [
            'name' => 'required|max:255',
            'email' => 'required|email|max:255|unique:users',
            'password' => 'required|confirmed|min:6',
        ]);
    }

    protected function create(array $data)
    {
        return User::create([
            'name' => $data['name'],
            'email' => $data['email'],
            'password' => bcrypt($data['password']),
        ]);
    }
}    

默认的开箱即用的身份验证控制器使用的特性:

trait AuthenticatesUsers
{
   use RedirectsUsers;

   public function getLogin()
   {
        return view('auth.login');
    }

   public function postLogin(Request $request)
   {
        $this->validate($request, [
            'email' => 'required|email', 'password' => 'required',
        ]);

        $credentials = $this->getCredentials($request);

        if (Auth::attempt($credentials, $request->has('remember'))) {
            return redirect()->intended($this->redirectPath());
        }

        return redirect($this->loginPath())
            ->withInput($request->only('email', 'remember'))
            ->withErrors([
               'email' => $this->getFailedLoginMessage(),
            ]);
   }

    public function loginPath()
   {
        return property_exists($this, 'loginPath') ? $this->loginPath : '/auth/login';
    }

}

trait RegistersUsers
{
    use RedirectsUsers;

    public function getRegister()
    {
        return view('auth.register');
    }

    public function postRegister(Request $request)
    {
        $validator = $this->validator($request->all());

        if ($validator->fails()) {
            $this->throwValidationException(
                $request, $validator
            );
         }

         Auth::login($this->create($request->all()));

        return redirect($this->redirectPath());
    }
}

我相信对于许多网络应用程序来说,这是一个非常普遍的要求,但我找不到任何有关Laravel特定实现的有用教程。所有教程都简单地关注于开箱即用的实现方式,原因很奇怪。

如果您能提供任何帮助,将不胜感激。


你的问题应该进行更正,我认为这被称为角色,这不是你要找的吗?这意味着你拥有相同的用户登录系统,但根据你的角色,你将对某些资源具有有限访问权限、完全访问权限或无访问权限。如果是这种情况,请告诉我。 - Maytham Fahmi
是的,这就是角色。因此,我有一个单一的用户表用于身份验证,其中包含基本信息(id、电子邮件、pwd)。我创建了两个名为jobseeker和recruiter的表,它们包含自己的属性。求职者和招聘人员都是用户类型,因此与用户表具有1对1的外键关系。求职者将具有不同类型的注册和登录表单,以区别于招聘人员。在注册时,求职者将被分配求职者角色。如何使用单一身份验证来检查角色并返回正确的登录和注册表单,并重定向到仪表板? - adam78
5个回答

6
这不是直接回答你的问题的解决方案,而是用另一种方式来解决你的问题。不同于为不同组创建不同的用户名和密码,可以创建一个拥有角色的中央认证系统,称之为“用户和角色”。
你可以定义拥有不同角色的不同组,并且每个角色都可以访问其各自的特定区域。
关于注册流程,您可以使用相同的控制器创建两个不同的视图,并为每个视图创建一个隐藏字段以指示它是求职者组或招聘者组。
两者都将收到两个不同的确认电子邮件,在那里他们应该填写其余的个人资料信息,例如招聘者应填写公司名称,求职者应填写自己的姓名等。他们可能会有两个不同的个人资料信息表,但仍然使用相同的登录系统。
通过向中间件添加条件和正确的路由,如果求职者尝试访问招聘者区域,即使求职者已经在系统中登录,求职者也无法访问该区域,反之亦然。
自从Laravel 5.1 内置用户登录系统以来,您有几个选择,构建自己的角色或使用第三方。
我建议您自己构建,这样您就可以控制代码并随着时间的推移进一步开发它。可能需要半天时间才能使其运行并理解其工作原理,但是正确的方法值得花费时间,而不是按照您在问题OR中所做的方式使用第三方,也可以,周围有很多包可供搜索。我个人使用过Entrust(https://github.com/Zizaco/entrust),它是为项目提供角色和权限的简单而美好的方式。
这里还有一个视频链接,由Laracast的Jeffrey Way开发,它从头开始构建Laravel 4的用户和角色系统。但是,既然您已经有了用户部分,请按照角色部分进行,并进行小修改,您将拥有适用于Laravel 5.1的角色系统,我已尝试过,它可以工作。
关于您在评论中提出的问题,当您观看视频后就会理解这个概念。
视频链接:https://laracasts.com/lessons/users-and-roles 您可能需要创建帐户才能观看视频,大多数视频都是免费的。
良好的实践: 展示您想要实现的内容总是一个好习惯,这样可以使事情变得更容易。我为您的项目制作了一个示例,但那只是学习的例子。

enter image description here

我鼓励您阅读有关角色的一些主题,这里您还可以找到一些启发 Laravel 的第三方 ACL 系统的文章,可能会有更多文章,但这里是一些:
阅读:
https://laracasts.com/discuss/channels/laravel/which-package-is-best-for-roles-permissions/?page=2
https://laracasts.com/discuss/channels/general-discussion/laravel-5-user-groups-management
https://laracasts.com/discuss/channels/general-discussion/roles-and-permissions-in-laravel-5

编辑

重要提示
Laravel 5.1引入了授权机制,目前网上的文档较少,但是值得花些时间去学习它:

http://laravel.com/docs/5.1/authorization#policies

新更新
有一些很棒的视频解决方案可以回答你的问题,请在此处关注ACL部分 https://laracasts.com/series/whats-new-in-laravel-5-1 这也可能非常有趣: https://laracasts.com/lessons/email-verification-in-laravel 这将为您提供完整的自主开发解决方案。

在定义 Eloquent 关系时,您会针对用户模型还是个人资料模型(例如 recruiter_profile)进行定义?例如,招聘人员有许多工作,而工作属于招聘人员,因此我应该针对 recruiter_profile 还是 user 模型定义关系,因为并非所有用户都发布工作? - adam78
你可以这样做,我最好的建议是为你的数据库制作ER图,然后将其反映在Laravel项目中。现在你正在以相反的方式工作,因此你正在工作的方式可能会给你带来设计上的困扰。但是当所有这些都说完之后,不要害怕,使用试错的概念进行工作是可以的,这样你会学到更多。所以长话短说,一个用户账户有一个个人资料,并且每个用户账户至少拥有一个角色,如果用户资料具有招聘者状态,则该账户能够创建工作。 - Maytham Fahmi
顺便提一下,试着看看这个问题,它是今天发布的,其中有一些元素可以在您的项目中重复使用,或者可能会给您带来一些灵感。https://dev59.com/EI7ea4cB1Zd3GeqPDZx3 - Maytham Fahmi

2

您可以轻松地通过调用sarav/laravel-multiauth包来实现多重身份验证

 composer require sarav/laravel-multiauth

我假设你有独立的表格用于求职者、招聘者和管理员。
步骤1:打开app.php并替换。
Illuminate\Auth\AuthServiceProvider::class

使用

Sarav\Multiauth\MultiauthServiceProvider::class

然后,打开auth.php文件并删除


<?php

return [

    'driver' => 'eloquent',
    'model'  => 'App\User::class',
    'table'  => 'users',
    'password' => [
       'email' => 'emails.password',
       'table' => 'password_resets',
       'expire' => 60,
    ],
];

并添加以下代码

return [
'multi' => [
    'jobseeker' => [
        'driver' => 'eloquent',
        'model'  => App\Jobseeker::class, // Model Class
        'table'  => 'jobseeker' // jobseeker table
    ],
    'recruiter' => [
        'driver' => 'eloquent',
        'model'  => App\Recruiter::class, // Model Class
        'table'  => 'recruiter' //recruiter table
    ],
    'admin' => [
        'driver' => 'eloquent',
        'model'  => App\Admin::class, // Model Class
        'table'  => 'admin' //admin table
    ],
 ],
 'password' => [
       'email' => 'emails.password',
       'table' => 'password_resets',
       'expire' => 60,
  ]
 ];

就是这样了!

现在你可以通过调用登录尝试来进行尝试。

\Auth::attempt('jobseeker', ['email'=> 'johndoe@example.com', 'password' => 'secret']);

\Auth::attempt('recruiter', ['email'=> 'johndoe@example.com', 'password' => 'secret']);

\Auth::attempt('admin', ['email'=> 'johndoe@example.com', 'password' => 'secret']);

始终记得第一个参数应该是用户参数。在这里,我为求职者登录尝试提供了求职者,为招聘人员尝试提供了招聘人员,为管理员登录尝试提供了管理员。如果没有正确的第一个参数,系统将抛出异常。

如需更详细信息,请查看此文章http://sarav.co/blog/multiple-authentication-in-laravel-continued/


请勿在多个问题中发布相同的答案。发表一个好的答案,然后投票/标记关闭其他重复的问题。如果问题不是重复的,请根据问题量身定制您的答案 - Martijn Pieters
我正在尝试使用这个包。一开始使用起来非常容易和直接,但是例如如何处理策略呢?内部函数authorize使用Guard类获取User类。现在你怎么为一个Account类编写策略呢?中间件也是同样的问题。通常你会注入Guard类,但这并不起作用,所以你必须使用\Auth::user('account')。是的,这可以工作,但代码质量不是很好。 - Michiel

0

我将尝试解释Laravel 5.1中身份验证是如何管理的。

在应用程序启动时,将调用AuthServiceProvider,该服务提供者调用registerAuthenticator()函数,在其中创建new AuthManager

AuthServiceProvider -> registerAuthenticator() -> new AuthManager()

在管理器创建时,将调用createNameDriver()函数,其中将创建新的nameProvider,其中name是在auth配置文件中选择的身份验证驱动程序。然后在该函数中,将创建一个新的Guard,并将nameProivder传递给其构造函数。该Guard中的所有身份验证函数都将使用该提供程序中的函数来管理身份验证。提供程序实现了UserProvider,其中包含

  • retrieveById($identifier)
  • retrieveByToken($identifier, $token)
  • updateRememberToken(Authenticatable $user, $token)
  • retrieveByCredentials(array $credentials)
  • validateCredentials(Authenticatable $user, array $credentials)

函数。

在Laravel 5.1中管理多个身份验证的主要思想是创建新的AutServiceProvider,并在其启动时将app auth传递给新的AuthModelProvider,然后在同一个Guard中使用这些函数。在AuthModelProvider中,您可以根据需要管理所有检索函数。

这是我对管理多个身份验证所做的所有更改。我的项目名称是APC,所以我在各个地方都使用它。

将此函数添加到您的模型中

public function getAuthIdentifier()
{
    return [self::MODULE_NAME => $this->getKey()];
}

在 Provider/YourProjectName 目录下创建 AuthServiceProvider。在 boot 函数中,我们从我们的新提供程序 AuthModelProvider 扩展 auth。
<?php

namespace App\Providers\Apc;

use Illuminate\Contracts\Auth\Access\Gate as GateContract;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Hashing\BcryptHasher;

class AuthServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap the application services.
     *
     * @return void
     */
    public function boot()
    {
        self::getAuthModels();
        $this->app['auth']->extend('apc', function() {
            return new AuthModelProvider(self::getAuthModels(), new BcryptHasher());
        });
    }

    /**
     * Register the application services.
     *
     * @return void
     */
    public function register()
    {

    }

    public static function getAuthModels()
    {
        $configModels = config('auth.models');
        $authModels = [];
        foreach ($configModels as $key => $class) {
            $authModel = new $class();
            $authModels [$key]=  $authModel;
        }

        return $authModels;
    }
}

在同一目录下创建AuthModelProvider。我的模型与其他模型的不同之处在于公司表中存在登录字段。但如果您愿意,您可以更具体地说明。在retrieveByCridentials函数中,我只是查找登录是否存在,并相应地选择我的模型。
<?php

namespace App\Providers\Apc;

use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Contracts\Hashing\Hasher as HasherContract;
use Illuminate\Support\Str;

class AuthModelProvider implements UserProvider
{
    protected $users;

    protected $hasher;

    public function __construct($usersModels, HasherContract $hasher)
    {
        $this->users = $usersModels;
        $this->hasher = $hasher;
    }

    /**
     * Retrieve a user by their unique identifier.
     *
     * @param  mixed $identifier
     * @return \Illuminate\Contracts\Auth\Authenticatable|null
     */
    public function retrieveById($identifiers)
    {
        foreach ($identifiers as $key => $id) {
            if (isset($this->users[$key])) {
                return $this->users[$key]->where('id', $id)->active()->base()->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($identifiers, $token)
    {
        return null;
        $user = $this->getUserByIdentifier($identifiers);
        if ($user) {
            return $user->where($user->getRememberTokenName(), $token)->active()->first();
        }
    }

    /**
     * Update the "remember me" token for the given user in storage.
     *
     * @param  \Illuminate\Contracts\Auth\Authenticatable $user
     * @param  string $token
     * @return void
     */
    public function updateRememberToken(Authenticatable $user, $token)
    {
        $user->setRememberToken($token);
        $user->save();
    }

    /**
     * Retrieve a user by the given credentials.
     *
     * @param  array $credentials
     * @return \Illuminate\Contracts\Auth\Authenticatable|null
     */
    public function retrieveByCredentials(array $credentials)
    {
        if (empty($credentials)) {
            return null;
        }

        if (isset($credentials['login'])) {
            $userModel = $this->users['company'];
        } else {
            $userModel = $this->users['user'];
        }

        $query = $userModel->newQuery();

        foreach ($credentials as $key => $value) {
            if (! Str::contains($key, 'password')) {
                $query->where($key, $value);
            }
        }

        return $query->first();
    }

    /**
     * Validate a user against the given credentials.
     *
     * @param  \Illuminate\Contracts\Auth\Authenticatable $user
     * @param  array $credentials
     * @return bool
     */
    public function validateCredentials(Authenticatable $user, array $credentials)
    {
        $plain = $credentials['password'];

        return $this->hasher->check($plain, $user->getAuthPassword());
    }

    private function getUserByIdentifier($identifiers)
    {
        if (!$identifiers) {

        }

        foreach ($identifiers as $namespace => $id) {
            if (isset($this->users[$namespace])) {
                return $this->users[$namespace];
            }
        }
        return null;
    }

}

将AuthServiceProvider添加到应用程序配置文件中。
 \App\Providers\Apc\AuthServiceProvider::class,

将此更改应用于auth配置文件。

'driver' => 'apc',

'models' => [
        \App\Apc\User\User::MODULE_NAME => \App\Apc\User\User::class,
        \App\Apc\Company\Company::MODULE_NAME => \App\Apc\Company\Company::class
],

就这些了。希望对你有所帮助。


0

简短回答:在您的用户表中添加具有特定数字的用户类型。

简短回答。

详细回答

  1. 如果您已经迁移了您的表,请运行 php artisan migrate:rollback
  2. 为用户的迁移表添加以下行:$table->integer("user_type")->default(0);

在这里,我认为用户类型零只是一个简单的求职者。

  1. 在您的表单中,您可以添加值为零和一的选项,这样人们就会选择他们想要像招聘人员一样。没有其他需求。

仅仅添加用户类型是不够的,因为不同类型的用户拥有不同的属性。 - adam78
@ozzii 我总是这样做。我不知道你所说的“属性”是什么意思。 - Aditya Giri

0
作为另一种解决方案,我可以建议您在用户和账户之间使用多态关系,例如:
class User extends Eloquent {

  ...

  public function account() {
    return $this->morphTo();
  }

}

class Account extends Eloquent {

  ...

  public function user() {
    return $this->morphOne(App\User::class, 'account');
  }
}

class JobSeeker extends Account { ... }

class Recruiter extends Account { ... }

对于不同类型的账户,您可以使用路由前缀和不同的授权控制器,特别是针对每个账户实例的注册方式不同:

// Recruiter Authentication routes...
Route::group(['prefix' => 'recruiter'], function() {
  Route::controller('auth', 'Auth\RecruiterAuthController');
});

最后,您可以直接从auth()->user()->account访问经过身份验证的帐户。它将返回任何Account(招聘人员,管理员等)的实例。
希望能对您有所帮助;)

使用用户、招聘人员、求职者、角色和角色用户表是否可以实现相同的功能?不同类型的用户可以被赋予不同的角色,例如招聘人员、求职者、管理员,并且与用户表具有1对1的关系。并且可以通过访问经过身份验证的帐户 auth()->user()->hasRole($role) 来实现。 - adam78
创建一个新的求职者账户的语法是什么? - adam78
另外,使用多态关系的话,就没有办法删除用户,即无法进行级联删除,因为用户表上的account_id可能与多种类型的账户相关联。我喜欢这个解决方案,但更多关于如何克服这些障碍的信息会很有帮助。 - adam78
多态关系使用两个列 account_idaccount_type 来存储相关模型的类名。首先应该创建求职者模型,然后创建用户并将其与求职者关联起来,使用 $jobseeker->user()->save(new User([crendentials]))。对于级联删除,可以在 $jobseeker->user()->delete() 中使用模型事件处理程序(deleting 或 deleted)。 - absolux
作为 ACL 的替代方案,您可以使用路由中间件来检查授权帐户类型的不同应用程序部分。 auth()->user()->account instanceof JobSeeker - absolux

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接