文章转发自专业的Laravel开发者社区,原始连接:learnku.com/laravel/t/2…php
若是你使用 Laravel 有一段时间了,你应该听过不少关于多种身份认证的信息。你也应该听过不少认证看守器。可是若是你对 Laravel 还不熟悉,多种身份认证可让不一样类别的用户访问相同应用的不一样/相似的部分。css
你可能但愿在你的 Laravel 应用程序中使用多种身份认证的缘由有不少。例如,你有一个运行整个公司的大型应用程序。客户还经过相同的应用程序与公司的产品和服务进行交互。该应用程序应该还有一个博客,公司中还有一个部门专门管理这个博客。html
从上面的程序能够看出,这里面已经有三组用户。客户,咱们可让他们使用一个特定的身份验证过程来访问应用;做者,他们应该有另外一个身份验证过程,甚至有权限来启用一个更健壮的内容管理流程;公司的其余成员,您应该根据他们不一样的角色来给他们展现不一样的功能菜单。 如今,让咱们看看如何为不一样类别的用户建立多个身份验证。mysql
若是你已经知足上面清单上的全部条件,那么你能够继续本教程, 咱们将建立拥有三个用户类的应用程序 — admin
,writer
,user
。咱们将为这三个用户类创建不一样的 guards
限制。laravel
咱们建立一个新的Laravel
应用程序,在终端执行下面的命令能够建立一个新的Laravel
应用程序。git
$ laravel new multi-auth
$ cd multi-auth
复制代码
咱们在应用程序中使用SQLite
数据库。 它是一个轻量级而且很是快的文件类型数据库。 咱们能够在命令行中建立数据库:github
$ touch database/database.sqlite
复制代码
在你的应用中,打开 .env
文件并把下面的配置:web
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=homestead
DB_USERNAME=homestead
DB_PASSWORD=secret
复制代码
修改成:sql
DB_CONNECTION=/absolute/path/to/database.sqlite
复制代码
这将会确保让咱们的应用使用SQLite
数据库驱动程序。数据库
咱们将会为admins
和 writers
表建立数据表迁移文件,相似users
表的迁移文件。这都是简单的用户表,未来你能够根据你的需求来扩展他们。
admins
数据表迁移文件运行下面的命令建立admins
数据表迁移文件:
$ php artisan make:migration create_admins_table
复制代码
在 database/migrations
目录, 打开并修改admins
数据表迁移文件:
// database/migrations/<timestamp>_create_admins_table.php
[...]
public function up()
{
Schema::create('admins', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('email')->unique();
$table->string('password');
$table->boolean('is_super')->default(false);
$table->rememberToken();
$table->timestamps();
});
}
[...]
复制代码
咱们建立了一个简单的数据表迁移文件,并定义了咱们要使用的数据表的字段。Eloquent
为咱们提供了方法来定义数据库表字段的数据类型。咱们使用它们定义表字段的数据类型。
记住,你能够按你的想法配置你的表。
经过如下命令来建立 writers
的数据迁移表:
$ php artisan make:migration create_writers_table
复制代码
打开刚才建立的迁移表文件进行修改:
database/migrations/<timestamp>_create_writers_table.php
[...]
public function up()
{
Schema::create('writers', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('email')->unique();
$table->string('password');
$table->boolean('is_editor')->default(false);
$table->rememberToken();
$table->timestamps();
});
}
[...]
复制代码
咱们仅仅建立了一个简单的迁移文件并定义了几个须要的字段。Eloquent
提供了不少定义数据库表字段类型的方法,因此操做起来比较容易。
如今咱们已经定义了表,咱们开始迁移数据库:
$ php artisan migrate
复制代码
咱们的应用程序有不一样类别的用户,他们存储在不一样的数据库表中,要为使用这些不一样的表的用户进行身份验证,咱们必须为它们定义模型。这些模型相似于用户模型并扩展可验证类。
执行下面的命令,建立 Admin 模型:
$ php artisan make:model Admin
复制代码
打开 Admin 模型文件 app/Admin.php
,添加以下代码:
// app/Admin.php
<?php
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class Admin extends Authenticatable
{
use Notifiable;
protected $guard = 'admin';
protected $fillable = [
'name', 'email', 'password',
];
protected $hidden = [
'password', 'remember_token',
];
}
复制代码
当你要对一个模型的访问作用户认证,但又不想用默认的 user
看守器时,就须要指定看守器。在这个例子中,这个看守器就是 admin
。
fillable
数组中,指定了用户能够操做的数据库字段。也就是明确地告知 Laravel:
当我执行
create
和update
方法时,我会把要操做的字段以数组的形式传给你,可是你只能够往数据库中插入fillable
数组中指定的字段。
这样,就能够防止用户任意操做咱们不但愿被更改的字段。
在 hidden
数组中,能够指定不但愿被返回的字段。
开始为 Writer
建立模型,咱们运行下面的命令:
$ php artisan make:model Writer
复制代码
接着咱们打开 Writer
模型,用下面的代码替换掉:
// app/Writer.php
<?php
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class Writer extends Authenticatable
{
use Notifiable;
protected $guard = 'writer';
protected $fillable = [
'name', 'email', 'password',
];
protected $hidden = [
'password', 'remember_token',
];
}
复制代码
Laravel
的守卫定义了如何为每一个请求进行身份验证。默认有一些用于身份验证的守卫,但也能够自定义。 这样可使用 Laravel
默认的认证系统和自定义的 Admin
和 Writer
模型。
打开 config/auth.php
并添加新的守卫,以下:
// config/auth.php
<?php
[...]
'guards' => [
[...]
'admin' => [
'driver' => 'session',
'provider' => 'admins',
],
'writer' => [
'driver' => 'session',
'provider' => 'writers',
],
],
[...]
复制代码
添加了两个新的守卫 admin
和 writer
,并设置了提供者。经过提供者来进行身份验证。
如今,完善 providers 数组:
// config/auth.php
[...]
'providers' => [
[...]
'admins' => [
'driver' => 'eloquent',
'model' => App\Admin::class,
],
'writers' => [
'driver' => 'eloquent',
'model' => App\Writer::class,
],
],
[...]
复制代码
根据上一步定义的看守器,咱们具体完善了 providers 数组的信息。用 eloquent
做为驱动,是由于咱们要用 Eloquent ORM 与数据库交互。
假设咱们要用别的 ORM 好比 RedBeanPHP 来操做数据库,那就要把驱动设置为 redbeanphp
,而不是 eloquent
。 至于 model
,就是具体要进行访问验证的模型类。
为了使这些看守器发挥各自的做用,咱们有两种方案能够选择,一是修改现有的验证控制器,二是建立新的控制器。你能够根据具体须要进行选择。在这个例子中,咱们选择前者。
打开 LoginController
,并作以下编辑:
// app/Http/Controllers/Auth/LoginController.php
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
[...]
use Illuminate\Http\Request;
use Auth;
[...]
class LoginController extends Controller
{
[...]
public function __construct()
{
$this->middleware('guest')->except('logout');
$this->middleware('guest:admin')->except('logout');
$this->middleware('guest:writer')->except('logout');
}
[...]
}
复制代码
经过中间件的方式限制访问控制器里的方法。咱们细分了访客的类型,这样,以一个类型的用户登陆以后,若是再想切换身份,以另外一种类型的用户登陆时,就会被引导到预约义的身份验证页面。
举个例子: 若是我在电脑上以管理员的身份登陆了,个人做家同事就没法以做者的身份登陆他的帐户。
这个验证操做是很重要的,这样就不会由于 session 信息的混乱,而给应用数据带来潜在的风险。
如今定义 admins 登录:
// app/Http/Controllers/Auth/LoginController.php
[...]
public function showAdminLoginForm()
{
return view('auth.login', ['url' => 'admin']);
}
public function adminLogin(Request $request)
{
$this->validate($request, [
'email' => 'required|email',
'password' => 'required|min:6'
]);
if (Auth::guard('admin')->attempt(['email' => $request->email, 'password' => $request->password], $request->get('remember'))) {
return redirect()->intended('/admin');
}
return back()->withInput($request->only('email', 'remember'));
}
[...]
复制代码
咱们已经设置了一个方法来返回管理员的登陆页面。 咱们将对全部用户类型使用相同的页面,并仅更改它们发送到的 URL 。 为咱们节省了许多咱们能够避免编写的代码。
咱们还定义了 adminLogin
方法,该方法检查是否提供了正确的凭据。 而后咱们尝试使用 admin
guard 登陆用户。 在尝试登陆时设置此守卫很是重要,以便Auth Facade将检查正确的表匹配凭据。 它还将设置咱们的身份验证,以便咱们能够根据登陆用户的类型限制页面。 咱们将通过身份验证的用户重定向到特定 URL,并将未经身份验证的用户返回登陆页面。
如今,让咱们为 writers 也作一样的事情:
// app/Http/Controllers/Auth/LoginController.php
[...]
public function showWriterLoginForm()
{
return view('auth.login', ['url' => 'writer']);
}
public function writerLogin(Request $request)
{
$this->validate($request, [
'email' => 'required|email',
'password' => 'required|min:6'
]);
if (Auth::guard('writer')->attempt(['email' => $request->email, 'password' => $request->password], $request->get('remember'))) {
return redirect()->intended('/writer');
}
return back()->withInput($request->only('email', 'remember'));
}
[...]
复制代码
咱们的登陆设置好了。万岁!!!
打开 RegisterController
并进行以下编辑:
// app/Http/Controllers/Auth/RegisterController.php
<?php
[...]
namespace App\Http\Controllers\Auth;
use App\User;
use App\Admin;
use App\Writer;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Http\Request;
[...]
class RegisterController extends Controller
{
[...]
public function __construct()
{
$this->middleware('guest');
$this->middleware('guest:admin');
$this->middleware('guest:writer');
}
[...]
}
复制代码
咱们已经设置了中间件,控制器未来会使用的到,就像咱们使用 LoginController
同样。
如今,让咱们设置为不一样用户返回不一样的注册页面的方法:
// app/Http/Controllers/Auth/RegisterController.php
[...]
public function showAdminRegisterForm()
{
return view('auth.register', ['url' => 'admin']);
}
public function showWriterRegisterForm()
{
return view('auth.register', ['url' => 'writer']);
}
[...]
复制代码
这跟咱们在显示不一样登陆页面时所作的相似。
如今,咱们能够定义一个建立 admin
的方法:
// app/Http/Controllers/Auth/RegisterController.php
[...]
protected function createAdmin(Request $request)
{
$this->validator($request->all())->validate();
$admin = Admin::create([
'name' => $request['name'],
'email' => $request['email'],
'password' => Hash::make($request['password']),
]);
return redirect()->intended('login/admin');
}
[...]
复制代码
接下来,让咱们定义建立 writer
的方法:
// app/Http/Controllers/Auth/RegisterController.php
[...]
protected function createWriter(Request $request)
{
$this->validator($request->all())->validate();
$writer = Writer::create([
'name' => $request['name'],
'email' => $request['email'],
'password' => Hash::make($request['password']),
]);
return redirect()->intended('login/writer');
}
[...]
复制代码
注册完成。
咱们将用 Laravel 自带的脚手架为验证系统生成页面和控制器。执行以下命令:
$ php artisan make:auth
复制代码
这样就能够生成模版文件 resources/views/auth
, 同时还会生成相应的路由,从而完成基本的验证操做。很酷对不对?
打开 login.blade.php
文件并作以下编辑:
// resources/views/auth/login.blade.php
[...]
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header"> {{ isset($url) ? ucwords($url) : ""}} {{ __('Login') }}</div>
<div class="card-body">
@isset($url)
<form method="POST" action='{{ url("login/$url") }}' aria-label="{{ __('Login') }}">
@else
<form method="POST" action="{{ route('login') }}" aria-label="{{ __('Login') }}">
@endisset
@csrf
[...]
</div>
复制代码
这里咱们会判断是否有 url
参数。若是有,就把这个参数带到表单提交的路由里。同时也会修改表单页的头部信息,从而实现根据不一样的登陆参数展现用户的类型。
打开 register.blade.php
模板文件并编辑,以下:
// resources/views/auth/register.blade.php
[...]
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header"> {{ isset($url) ? ucwords($url) : ""}} {{ __('Register') }}</div>
<div class="card-body">
@isset($url)
<form method="POST" action='{{ url("register/$url") }}' aria-label="{{ __('Register') }}">
@else
<form method="POST" action="{{ route('register') }}" aria-label="{{ __('Register') }}">
@endisset
@csrf
[...]
</div>
复制代码
咱们复制了登陆页面的操做。
如今,咱们已经完成了登陆和注册页面的设置,让咱们让 admin 和 writers 在验证页面时看到这些页面。打开终端并运行如下命令来建立新文件。接下来,咱们将把相应的代码片断插入到文件中。
$ touch resources/views/layouts/auth.blade.php
$ touch resources/views/admin.blade.php
$ touch resources/views/writer.blade.php
$ touch resources/views/home.blade.php
复制代码
将此代码块插入 auth.blade.php
文件:
// resources/views/layouts/auth.blade.php
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- CSRF Token -->
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>{{ config('app.name', 'Laravel') }}</title>
<!-- Scripts -->
<script src="{{ asset('js/app.js') }}" defer></script>
<!-- Fonts -->
<link rel="dns-prefetch" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css?family=Raleway:300,400,600" rel="stylesheet" type="text/css">
<!-- Styles -->
<link href="{{ asset('css/app.css') }}" rel="stylesheet">
</head>
<body>
<div id="app">
<nav class="navbar navbar-expand-md navbar-light navbar-laravel">
<div class="container">
<a class="navbar-brand" href="{{ url('/') }}">
{{ config('app.name', 'Laravel') }}
</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="{{ __('Toggle navigation') }}">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<!-- Left Side Of Navbar -->
<ul class="navbar-nav mr-auto">
</ul>
<!-- Right Side Of Navbar -->
<ul class="navbar-nav ml-auto">
<!-- Authentication Links -->
<li class="nav-item dropdown">
<a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre>
Hi There <span class="caret"></span>
</a>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
<a class="dropdown-item" href="{{ route('logout') }}"
onclick="event.preventDefault(); document.getElementById('logout-form').submit();">
{{ __('Logout') }}
</a>
<form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
@csrf
</form>
</div>
</li>
</ul>
</div>
</div>
</nav>
<main class="py-4">
@yield('content')
</main>
</div>
</body>
</html>
复制代码
接下来, 将此代码块插入 admin.blade.php
文件:
// resources/views/admin.blade.php
@extends('layouts.auth')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">Dashboard</div>
<div class="card-body">
Hi boss!
</div>
</div>
</div>
</div>
</div>
@endsection
复制代码
打开 writer.blade.php
模板文件并编辑,以下:
// resources/views/writer.blade.php
@extends('layouts.auth')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">Dashboard</div>
<div class="card-body">
Hi there, awesome writer
</div>
</div>
</div>
</div>
</div>
@endsection
复制代码
最后, 打开 home.blade.php
模板文件并替换为:
// resources/views/home.blade.php
@extends('layouts.auth')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">Dashboard</div>
<div class="card-body">
Hi there, regular user
</div>
</div>
</div>
</div>
</div>
@endsection
复制代码
咱们的应用程序准备差很少了。让咱们定义到目前为止建立的全部方法的路由。打开 routes/web.php
并替换为:
// routes/web.php
<?php
Route::view('/', 'welcome');
Auth::routes();
Route::get('/login/admin', 'Auth\LoginController@showAdminLoginForm');
Route::get('/login/writer', 'Auth\LoginController@showWriterLoginForm');
Route::get('/register/admin', 'Auth\RegisterController@showAdminRegisterForm');
Route::get('/register/writer', 'Auth\RegisterController@showWriterRegisterForm');
Route::post('/login/admin', 'Auth\LoginController@adminLogin');
Route::post('/login/writer', 'Auth\LoginController@writerLogin');
Route::post('/register/admin', 'Auth\RegisterController@createAdmin');
Route::post('/register/writer', 'Auth\RegisterController@createWriter');
Route::view('/home', 'home')->middleware('auth');
Route::view('/admin', 'admin');
Route::view('/writer', 'writer');
复制代码
当用户通过身份验证时,修改用户的重定向方式很是重要。默认状况下,Laravel将全部通过身份验证的用户重定向到 /home
。若是不修改重定向,将会获得下面的错误。
因此, 为了解决这个, 打开 app/Http/Controllers/Middleware/RedirectIfAuthenticated.php
文件并替换为:
// app/Http/Controllers/Middleware/RedirectIfAuthenticated.php
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
class RedirectIfAuthenticated
{
public function handle($request, Closure $next, $guard = null)
{
if ($guard == "admin" && Auth::guard($guard)->check()) {
return redirect('/admin');
}
if ($guard == "writer" && Auth::guard($guard)->check()) {
return redirect('/writer');
}
if (Auth::guard($guard)->check()) {
return redirect('/home');
}
return $next($request);
}
}
复制代码
RedirectIfAuthenticated
中间件接收 auth 守卫做为参数。当咱们试图访问任何针对通过身份验证的用户的页面时,将触发此中间件。而后,咱们能够肯定用户拥有的身份验证类型并相应地重定向它们。
当用户被重定向时会发生一件烦人的事情。若是用户试图访问 /writer
,但没有通过身份验证,那么用户会被重定向到 /login/writer
,他们被重定向到 /login
这不是咱们想要的。
为了确保当用户尝试访问 /writer
时,它们被重定向到 /login/writer
或者一样用于 /admin
,咱们必须修改异常处理程序。 在 app/Exceptions
中打开处理程序文件并添加如下内容:
// app/Exceptions/Handler.php
<?php
namespace App\Exceptions;
use Exception;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
[...]
use Illuminate\Auth\AuthenticationException;
use Auth;
[...]
class Handler extends ExceptionHandler
{
[...]
protected function unauthenticated($request, AuthenticationException $exception)
{
if ($request->expectsJson()) {
return response()->json(['error' => 'Unauthenticated.'], 401);
}
if ($request->is('admin') || $request->is('admin/*')) {
return redirect()->guest('/login/admin');
}
if ($request->is('writer') || $request->is('writer/*')) {
return redirect()->guest('/login/writer');
}
return redirect()->guest(route('login'));
}
}
复制代码
咱们刚刚添加的 unauthenticated
方法解决了咱们遇到的这个问题。 它默认接收一个 AuthenticationExpection
异常,它携带该保护信息。 遗憾的是,咱们没法访问它,由于它受到保护(但愿Laravel 5.7可以提供访问它的方法)。
咱们的解决方法是使用 request→is()
。这会检查咱们尝试访问的URL。若是咱们没有绝对URL或者咱们有路由组,它也能够检查URL模式。
在咱们的例子中,咱们首先检查是否收到了 JSON 请求并单独处理异常。 而后咱们检查咱们是否正在尝试访问 /admin
或任何以 admin
开头的 URL。 咱们将用户重定向到相应的登陆页面。 咱们也检查 writer
。
这对咱们来讲是一个很好的解决方法,但这意味着咱们必须知道咱们想要访问的绝对URL,或者至少对咱们的守卫保护的全部路由都有相同的前缀。
如今咱们的应用程序已经准备好了,运行下面的命令来启动它:
$ php artisan serve
复制代码
它一般应该是可用的 http://localhost:8000
.
先经过访问
http://localhost:8000/register/writer
和http://localhost:8000/register/admin
这两个连接来分别注册做者和管理员. 而后访问http://localhost:8000/login/writer
和http://localhost:8000/login/admin
这两个连接分别登陆做者和管理员。
在本教程中,咱们深刻研究了 Laravel 身份验证。 咱们定义了多个守卫来处理多个身份验证和访问控制。咱们还为通过身份验证的用户处理重定向,为未经身份验证的用户处理重定向。
若是您彻底遵循本指南,您将可以为具备不一样用户类型的应用程序(多是[多租户](en.wikipedia.org/wiki/Multit… 尽管如此,尝试扩展你所看到的并分享你想出的东西。
本文中应用程序的源代码可在 GitHub。