Laravel是一个“优雅的”PHP框架,其封装了一些常用功能,使得后端开发更加方便。这篇文章将从零介绍如何使用Laravel框架实现用户身份认证,保护网页中的受保护资源。
创建Laravel项目
推荐使用composer一键创建项目:
cd ~/www-test/
composer create-project laravel/laravel test5
尝试运行:
cd ~/www-test/test5
php artisan serve
默认在localhost的8000端口运行测试服务,浏览器打开,可以看到默认主页页面:
创建页面并设置路由
我们可以先创建几个简单的页面视图,并设置路由,查看效果。
创建页面,Laravel使用.blade.php的后缀命名视图文件:
我们使用authpage表示登陆页面,用来显示输入用户名和密码的表单;使用nonauth表示那些无需登陆即可查看的页面;使用secretpage表示需要认证才能查看的页面。
这些页面的内容可以临时随便填一下,主要是为了简单地弄懂认证状态下对各页面的路由。
编辑routes/web.php,按照逻辑将刚刚创建的几个页面添加进路由:
Route::get('/', function () {
return view('welcome');
});
Route::get('/login', function () {
return view('authpage.index');
})->name('login');
Route::get('/register', function () {
return view('register.index');
})->name('register');
Route::get('/nonauth', function () {
return view('nonauth.index');
});
Route::get('/secretpage', function () {
return view('secretpage.index');
})->middleware('auth');
当路由后,使用view显示视图,Laravel框架就会从resources/views中查找相应的视图文件。若使用子文件夹,使用点表示路径分隔,如本例。
接下来再次运行服务,分别访问以上三个URL,查看效果:
同样地,访问/secretpage,发现被重定向到了登陆页面。
认证重定向-展开说明
在这里,我们仅仅定义了路由,添加了几个页面,怎么就实现了对未登陆用户的重定向了呢?其实这一切是由Laravel中间件实现的。
当访问/secretpage时,匹配到了我们编写的路由规则,因此Laravel知道,要将这个请求交给名为auth的中间件来处理一下。但是名为auth的中间件在哪里呢?参考官方手册-Middleware,发现定义于app/Http/Kernel.php:
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
于是,Laravel框架就知道了auth中间件定义的命名空间,我们顺着命名空间查看一下这个中间件的代码,位于app/Http/Middleware/Authenticate.php:
<?php
namespace App\Http\Middleware;
use Illuminate\Auth\Middleware\Authenticate as Middleware; //2
class Authenticate extends Middleware
{
/**
* Get the path the user should be redirected to when they are not authenticated.
*
* @param \Illuminate\Http\Request $request
* @return string|null
*/
protected function redirectTo($request)
{
if (! $request->expectsJson()) {
return route('login'); //1
}
}
}
1处route(‘xxx’)返回某个命名路由的URL,通过调用这个方法,Laravel就知道要对未认证的用户重定向到哪里了。但此时你也许会纳闷,中间件的处理方法名称不应该是handle吗(参考官方文档-Middleware)?redirectTo方法应该不会被调用才对啊?
原来这里耍了个小花招,为了方便用户配置或定制,又避免在大量的代码中查找修改,2处使用了拓展类的方式来自定义中间件。查看真正的代码vendor/laravel/framework/src/Illuminate/Auth/Middleware/Authenticate.php:
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param string[] ...$guards
* @return mixed
*
* @throws \Illuminate\Auth\AuthenticationException
*/
public function handle($request, Closure $next, ...$guards)
{
$this->authenticate($request, $guards);
return $next($request);
}
所以,这里才是真正的中间件处理入口。
到这里打住!我们仅仅设置了个路由,还远未实现完整的认证系统!关于认证中间件的不再展开了,有兴趣的同学也可以自己读一读源代码,感受一下Laravel的“优雅”。
配置数据库
于config/database.php配置你的数据库连接,可以参考官方文档-数据库配置。
然后执行一下命令完成数据库迁移:
php artisan migrate
接下来需要设置session存储方式。本文使用数据库存储session:
php artisan session:table
php artisan migrate
实现注册
要实现注册操作,我们需要实现两个功能:一个是用于用户注册的接口,一个是用户注册页面。用户使用浏览器打开register页面,页面上展现一个表单,用户填写表单后,将数据发往注册API接口。
注册API实现
为了实现更加复杂的操作,应该增加一个处理用户的控制器:
php artisan make:controller UserController
添加API路由routes/api.php:
use App\Http\Controllers\UserController;
...
Route::post('/register', [UserController::class, 'register']);
这样,请求api/register时,路由将调用UserController的register方法。下面完成这个方法的设计。
app/Http/Controllers/UserController.php文件内容如下:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
// use App\Http\Requests\RegisterPostRequest;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\Hash;
use App\Models\User;
class UserController extends Controller
{
public function register( Request $request ) {
$validator = Validator::make($request->all(), [
'name' => ['required', 'unique:users,name', 'max:30'],
'email' => ['required', 'unique:users,email', 'email:rfc', 'max:255'],
'password' => ['required', 'confirmed'],
'password_confirmation' => ['required']
]);
// var_dump($validator->validated());
if ($validator->fails()) {
return response()->json([
'status' => 'failed',
'message' => $validator->errors(),
]);
} else {
$validated = $validator->validated();
$user = new User;
$user->name = $validated['name'];
$user->email = $validated['email'];
$user->password = Hash::make($validated['password']);
$user->save();
return response()->json([
'status' => 'success',
]);
}
}
}
思路大体上就是:首先使用Validator验证输入,若验证无误,使用User数据库模型存储数据,其中密码使用Hash加密。
使用Postman对注册接口进行请求,可以看到数据库添加了一行数据。
显然,最好通过邮件验证来防止恶意注册,且数据库也有这个邮箱激活时间的栏目。将在稍后实现这一功能。
注册页面实现
设计前端页面,使用表单或AJAX的方式向后端POST数据,即可完成注册,这里不再赘述,你也可以设计你自己的逻辑。
实现登陆
登陆接口实现
登陆实现更加简单。在UserController中编辑signin方法,就是照着官方文档这里写的,使用Auth Facade即可完成认证:
public function signin( Request $request ) {
$credentials = $request->validate([
'email' => ['required', 'email:rfc', 'max:255'],
'password' => ['required'],
]);
if (Auth::attempt($credentials)) {
$request->session()->regenerate();
return Redirect::to('/secretpage');
} else {
return back()->withErrors([
'email' => 'The provided credentials do not match our records.',
])->onlyInput('email');
}
}
然后在web中添加路由。登陆最好不要使用api路由实现,因为Laravel默认不开启api的session中间件,换句话说,默认情况下只有web路由组才开启了session中间件,才能正常认证。
在routes/web.php中添加路由如下:
Route::get('/signin', [UserController::class, 'signin']);
登陆页面实现
<div class="container">
<br>
<h1>登陆</h1>
<hr>
<ul>
@foreach($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
<form action="/signin" method="get">
<div class="mb-3">
<label for="email" class="form-label">Email</label>
<input type="email" class="form-control" name="email">
</div>
<div class="mb-3">
<label for="password" class="form-label">密码</label>
<input type="password" class="form-control" name="password">
</div>
<button type="submit" class="btn btn-primary">登陆</button>
</form>
</div>
简单地使用表单实现就好。
登陆后即可正常访问受保护的页面secretpage。
实现注销
不得不说,有了Auth Facade,用户管理流程被大大简化了。在UserController中编辑signout方法即可:
public function signout( Request $request ) {
if (Auth::check()) {
Auth::logout();
}
return Redirect::to('/login');
}
然后添加路由:
Route::get('/signout', [UserController::class, 'signout']);
退出登陆后再次访问secretpage,发现被重定向到了登陆页面,注销成功。
实现邮箱验证
参考官方文档-邮箱验证,发现实际上Laravel也为我们提供了基本的邮箱验证功能。仅需配置一些路由、视图即可。
添加web路由:
// 提示需要进行邮箱认证
Route::get('/email/verify', function () {
return view('auth.verify-email');
})->middleware('auth')->name('verification.notice');
// 认证链接
Route::get('/email/verify/{id}/{hash}', function (EmailVerificationRequest $request) {
$request->fulfill();
return redirect('/home');
})->middleware(['auth', 'signed'])->name('verification.verify');
// 重新发送认证邮件
Route::get('/email/verification-notificati on', function (Request $request) {
$request->user()->sendEmailVerificationNotification();
return back()->with('message', 'Verification link sent!');
})->middleware(['auth', 'throttle:6,1'])->name('verification.send');
然后按照路由添加一些视图,确保这些视图打得开。这里就不再赘述添加视图的方法。
修改User数据库模型:
class User extends Authenticatable implements MustVerifyEmail
最后,在UserController添加一个消息,使之在用户添加后执行:
$user->save();
event(new Registered($user));
鉴于现在已经完成对用户邮箱进行验证的功能,我们可以仅允许已认证的用户访问受保护的资源:
将
Route::get('/secretpage', function () {
return view('secretpage.index');
})->middleware('auth');
改为
Route::get('/secretpage', function () {
return view('secretpage.index');
})->middleware('verified');
即可。
调试和查看
推荐使用Laravel Telescope来调试发送的邮件。安装流程可参考官方文档-Telescope。
由此,一个略为完整的身份认证页面就搭建好了,当然了,为了让新手更快上手,这篇文章的例子有点简单。你完全可以发挥自己的想象力,搭建一个自己的认证系统,并且设计一个用户中心。