Laravel5.3 多表验证(包括登录注册,找回密码,发送邮件)
主要内容
我们在开发过程中,往往会需要前台和后台两个部分组成一个完整的项目,一些公司需要频繁的在短期内开发一个项目,往往这些项目的一部分功能是重复的,所以可以提炼出来做为模板,以供下个项目使用,之前有人发了登录部分的实现,今天想聊一下包括找回密码和发送邮件在内的一些细节,我们都通过 laravel
内置的验证来完成。
第一步
在一个初始的 laravel
框架中,运行这个 php artisan make:auth
来生成我们需要的基础验证路由,视图和控制器,我们初始化了一个 laravel
框架之后,运行该命令增加的文件如下。
更改结构,复制粘贴
我们需要将她复制为两个模块,一个是前台(以 web
命名),一个是后台(以 admin
命名)。
路由
Route::get('/', function () {
return view('welcome');
});
Route::group([
'namespace' => 'Web',
], function ($route) {
$route->get('/home', 'HomeController@index');
Auth::routes();
});
Route::group([
'prefix' => 'admin',
'namespace' => 'Admin',
], function ($route) {
Auth::routes();
$route->get('/', 'HomeController@index');
});
视图
在 views
下新建两个文件夹,一个为 web
一个是 admin
,将生成的 auth
文件夹复制,扔在这两个目录下,原来的删掉就好,将 home.blade.php
放在 web
下,再复制一份放在 admin
下,可以更改里边内容来区别用户和管理员界面,并且一定记得修改 admin
下的4个表单的 action
和 login
视图的 reset password
的链接,加上前缀 admin
views
修改好后如下:
控制器
和视图类似,在 Controllers
目录下新建两个文件夹 Web
和 Admin
,将 Auth
文件夹复制并扔在这两个文件夹里,删除原来的 Auth
,并把 HomeController.php
扔在 Web
目录下,并且复制一份扔在 Admin
并且修改这两个 HomeContrller
的 index
方法里 view('web.home')
和 view('admin.home')
,目录下此时注意修改命名空间,将这
10个控制器的 namespace
根据所在目录分别加上 Web
或 Admin
,以 Web/Auth/LoginController.php
为例,修改命名空间为 namespace App\Http\Controllers\Web\Auth;
复制粘贴的工作就做完了,此时的控制器如下:
可是此时打开登录界面会产生如下错误:
注意,我们可以看到 LoginController
里的 showLoginForm
方法,我们回到 Web/LoginController.php
可以看到她引用了一个 trait
:
<?php
namespace App\Http\Controllers\Admin\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
class LoginController extends Controller
{
/*
|--------------------------------------------------------------------------
| Login Controller
|--------------------------------------------------------------------------
|
| This controller handles authenticating users for the application and
| redirecting them to your home screen. The controller uses a trait
| to conveniently provide its functionality to your applications.
|
*/
use AuthenticatesUsers;
......
跟踪过去后是如下代码:
<?php
namespace Illuminate\Foundation\Auth;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Lang;
trait AuthenticatesUsers
{
use RedirectsUsers, ThrottlesLogins;
/**
* Show the application's login form.
*
* @return \Illuminate\Http\Response
*/
public function showLoginForm()
{
return view('auth.login');
}
......
}
这个 trait
很重要我们之后会覆盖这里的其他方法,此时因为我们在 views
下移走了 auth
目录,所以是错到了这里,可是这个 trait
是大家公共使用的,我们不能随便在此更改,因此需要在 LoginController
里覆盖这个方法,在 LoginController
里增加如下代码:
public function showLoginForm()
{
return view('web.auth.login');
}
这样就成功的走到了我们更改后的目录里,同理覆盖 RegisterController.php
里的 showRegistrationForm()
方法,以及对应的 Admin
目录下的控制器方法,控制器到这里先停一下,
中间件
我们需要聊点别的。此时用户的登录注册页面可以正常打开,但是管理员的却不行,因为我们此时走的是同一个验证的中间件,所以需要一个验证身份的中间件,新建一个 middleware
:AdminAuthMiddleware.php
如下:
(参考: 教程: Laravel 5.3 多用户表登录)
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
class AdminAuthMiddleware
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next, $guard = null)
{
if (Auth::guard($guard)->guest()) {
if ($request->ajax() || $request->wantsJson()) {
return response('Unauthorized.', 401);
} else {
return redirect()->guest('/admin/login');
}
}
return $next($request);
}
}
向 App\Http\Kernel
中的数组 $routeMiddleware
注册
/**
* The application's route middleware.
*
* These middleware may be assigned to groups or used individually.
*
* @var array
*/
protected $routeMiddleware = [
'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
'auth.admin' => \App\Http\Middleware\AdminAuthMiddleware::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
];
修改 RedirectIfAuthenticated.php
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
class RedirectIfAuthenticated
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param string|null $guard
* @return mixed
*/
public function handle($request, Closure $next, $guard = null)
{
if (Auth::guard($guard)->check()) {
// 根据不同 guard 跳转到不同的页面
$url = $guard ? 'admin/':'/';
return redirect($url);
}
return $next($request);
}
}
更改配置文件(其中涉及的模型请继续往下看):
config/auth.conf:
<?php
return [
/*
|--------------------------------------------------------------------------
| Authentication Defaults
|--------------------------------------------------------------------------
|
| This option controls the default authentication "guard" and password
| reset options for your application. You may change these defaults
| as required, but they're a perfect start for most applications.
|
*/
'defaults' => [
'guard' => 'web',
'passwords' => 'users',
],
/*
|--------------------------------------------------------------------------
| Authentication Guards
|--------------------------------------------------------------------------
|
| Next, you may define every authentication guard for your application.
| Of course, a great default configuration has been defined for you
| here which uses session storage and the Eloquent user provider.
|
| All authentication drivers have a user provider. This defines how the
| users are actually retrieved out of your database or other storage
| mechanisms used by this application to persist your user's data.
|
| Supported: "session", "token"
|
*/
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'admin' => [
'driver' => 'session',
'provider' => 'admins',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
],
],
/*
|--------------------------------------------------------------------------
| User Providers
|--------------------------------------------------------------------------
|
| All authentication drivers have a user provider. This defines how the
| users are actually retrieved out of your database or other storage
| mechanisms used by this application to persist your user's data.
|
| If you have multiple user tables or models you may configure multiple
| sources which represent each model / table. These sources may then
| be assigned to any extra authentication guards you have defined.
|
| Supported: "database", "eloquent"
|
*/
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
// 'users' => [
// 'driver' => 'database',
// 'table' => 'users',
// ],
'admins' => [
'driver' => 'eloquent',
'model' => App\Models\Admin::class,
],
],
/*
|--------------------------------------------------------------------------
| Resetting Passwords
|--------------------------------------------------------------------------
|
| You may specify multiple password reset configurations if you have more
| than one user table or model in the application and you want to have
| separate password reset settings based on the specific user types.
|
| The expire time is the number of minutes that the reset token should be
| considered valid. This security feature keeps tokens short-lived so
| they have less time to be guessed. You may change this as needed.
|
*/
'passwords' => [
'users' => [
'provider' => 'users',
'table' => 'password_resets',
'expire' => 60,
],
'admins' => [
'provider' => 'admins',
'table' => 'admin_password_resets',
'expire' => 60,
]
],
];
模型
在 app
目录下新建 Models
目录,把 User.php
复制一份改名为 Admin.php
,注意同时修改类名为Admin
,新建数据表:
php artisan make:migration create_admins_table
php artisan make:migration create_admin_password_resets_table
php artisan make:seeder UsersTableSeeder
php artisan make:seeder AdminsTableSeeder
他们分别长这样:
class CreateAdminsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('admins', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('email')->unique();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('admins');
}
}
class CreateAdminPasswordResetsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('admin_password_resets', function (Blueprint $table) {
$table->string('email')->index();
$table->string('token')->index();
$table->timestamp('created_at')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('admin_password_resets');
}
}
class AdminsTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
\App\Models\Admin::create([
'name' => '后台管理员',
'email' => 'admin@admin.com',
'password' => bcrypt('admin123'),
]);
}
}
class UsersTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
\App\Models\User::create([
'name' => '普通用户',
'email' => 'user@user.com',
'password' => bcrypt('123456'),
]);
}
}
之后迁移数据库,我们的模型就完成了,接下来继续完成控制器部分。
完善控制器
前台部分
Web/Auth/RegisterController.php
修改移了位置的 User
模型,前端登录功能不需要更改,注册功能改这里之后就完成了。
use App\Models\User;
Web/Auth/ForgotPasswordController.php
<?php
namespace App\Http\Controllers\Web\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
use Illuminate\Support\Facades\Password;
class ForgotPasswordController extends Controller
{
/*
|--------------------------------------------------------------------------
| Password Reset Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling password reset emails and
| includes a trait which assists in sending these notifications from
| your application to your users. Feel free to explore this trait.
|
*/
use SendsPasswordResetEmails;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest');
}
public function showLinkRequestForm()
{
return view('web.auth.passwords.email');
}
protected function guard()
{
return Auth::guard('web');
}
public function broker()
{
return Password::broker('users');
}
}
Web/Auth/ResetPasswordController.php
<?php
namespace App\Http\Controllers\Web\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ResetsPasswords;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Password;
class ResetPasswordController extends Controller
{
/*
|--------------------------------------------------------------------------
| Password Reset Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling password reset requests
| and uses a simple trait to include this behavior. You're free to
| explore this trait and override any methods you wish to tweak.
|
*/
use ResetsPasswords;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest');
}
public function showResetForm(Request $request, $token = null)
{
//这里根据需求来看是否需要把邮箱直接带到页面上
return view('web.auth.passwords.reset')->with(
['token' => $token, 'email' => $request->email]
);
}
public function broker()
{
return Password::broker('users');
}
}
在 .env
里配置自己的 mailtrap
https://mailtrap.io以供发邮件测试,登录网站后去看他随机给的用户名密码贴上去,加密不贴:
MAIL_DRIVER=smtp
MAIL_HOST=mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=
MAIL_PASSWORD=
MAIL_ENCRYPTION=
测试用户端找回密码可用。前台大功告成~
后台部分
Admin/Auth/LoginController.php
<?php
namespace App\Http\Controllers\Admin\Auth;
use Illuminate\Support\Facades\Auth;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
class LoginController extends Controller
{
/*
|--------------------------------------------------------------------------
| Login Controller
|--------------------------------------------------------------------------
|
| This controller handles authenticating users for the application and
| redirecting them to your home screen. The controller uses a trait
| to conveniently provide its functionality to your applications.
|
*/
use AuthenticatesUsers;
/**
* Where to redirect users after login.
*
* @var string
*/
protected $redirectTo = '/admin';
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest:admin', ['except' => [
'logout',
'redirectToLogin',
]]);
}
public function showLoginForm()
{
return view('admin.auth.login');
}
public function redirectToLogin()
{
if ($this->guard()->user()) {
return redirect('/admin/');
}
return redirect('/admin/login');
}
protected function guard()
{
return Auth::guard('admin');
}
public function logout(Request $request)
{
$this->guard()->logout();
$request->session()->flush();
$request->session()->regenerate();
return redirect('/admin/login');
}
}
Admin/Auth/ForgotPasswordController.php
<?php
namespace App\Http\Controllers\Admin\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
use Illuminate\Support\Facades\Password;
class ForgotPasswordController extends Controller
{
/*
|--------------------------------------------------------------------------
| Password Reset Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling password reset emails and
| includes a trait which assists in sending these notifications from
| your application to your users. Feel free to explore this trait.
|
*/
use SendsPasswordResetEmails;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest');
}
public function showLinkRequestForm()
{
return view('admin.auth.passwords.email');
}
protected function guard()
{
return Auth::guard('admin');
}
public function broker()
{
return Password::broker('admins');
}
}
邮件可以正常发送,但是邮件的内容里的链接不对啊,所以在模型 Models/Admin.php
里加入方法:
**
* 覆盖了Authenticatable里的trait CanResetPassword的方法
*
* @param string $token
*/
public function sendPasswordResetNotification($token)
{
$this->notify(new AdminResetPassword($token));
}
然后新建 Notification
:php artisan make:notification AdminResetPassword
生成文件 app/Notifications/AdminResetPassword.php
修改内容如下:
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
class AdminResetPassword extends Notification
{
use Queueable;
public $token;
/**
* Create a new notification instance.
*
* @return void
*/
public function __construct($token)
{
$this->token = $token;
}
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
* @return array
*/
public function via($notifiable)
{
return ['mail'];
}
/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
return (new MailMessage)
->line([
'您收到了这份邮件,是因为我们收到了关于您账户重置密码的请求。',
'点击下面的按钮重置密码:',
])
->action('重置密码', url('/admin/password/reset', $this->token))
->line('如过不是您本人操作的,请注意您的账户安全。');
}
/**
* Get the array representation of the notification.
*
* @param mixed $notifiable
* @return array
*/
public function toArray($notifiable)
{
return [
//
];
}
}
当然您也可以通过 Notification
自定义邮件视图,到此邮件可以正常发送。
Admin/Auth/ResetPasswordController.php
修改后可以正常重置密码,注册部分就不说了,一般情况后台管理员不需要注册,而且就算有业务需求,想必大家看到也能懂怎么改了。
<?php
namespace App\Http\Controllers\Admin\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ResetsPasswords;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Password;
class ResetPasswordController extends Controller
{
/*
|--------------------------------------------------------------------------
| Password Reset Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling password reset requests
| and uses a simple trait to include this behavior. You're free to
| explore this trait and override any methods you wish to tweak.
|
*/
use ResetsPasswords;
public $redirectTo = '/admin';
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest');
}
public function showResetForm(Request $request, $token = null)
{
return view('admin.auth.passwords.reset')->with(
['token' => $token, 'email' => $request->email]
);
}
protected function guard()
{
return \Auth::guard('admin');
}
public function broker()
{
return Password::broker('admins');
}
}
Admin/HomeController.php
在默认构造函数中进行权限判断,以助于在访问 /admin
的 url 的时候能正常跳转到 /admin/login
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class HomeController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('auth.admin:admin');
}
/**
* Show the application dashboard.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
return view('admin.home');
}
}
今天实在无聊,就写写博客啥的发泄一下。。。笔者边做边写的,理应没多大问题,其中涉及部分 laravel 的源码推荐大家可以看看,做的时候主要是 broker()
方法找不到位置,后来发现是在 app.php
的 providers
数组中注册的一个 service provider
Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,
,不懂得可以留言,有遗漏的地方可以留言,笔者定会及时改正~