本文介绍 Laravel 用户认证的使用、基本逻辑和底层的实现。
快速使用
在 Laravel 框架初始化后,运行 php artisan make:auth
和 php artisan migrate
就能启用 Laravel 自带的用户认证功能。
- 用户认证的配置文件在
config/auth.php
中,
// 默认使用的配置。
// guard 是用户认证逻辑的实现。web 是网站用户认证逻辑;api 是 API 用户认证逻辑
'defaults' => [
'guard' => 'web',
'passwords' => 'users',
],
guard
还需配置对应的 driver
和 provider
。
driver
配置对应具体实现 guard
的逻辑类;provider
是逻辑类使用的数据提供者(查询、更新对应的用户信息的具体实现类)。
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
...
],
- 认证的路由信息在
Illuminate\Routing\Router.php
中,通过Illuminate\Support\Facades\Auth
的routes()
方法引入。注册和登录部分如下:
public function auth()
{
// Authentication Routes...
$this->get('login', 'Auth\LoginController@showLoginForm')->name('login');
$this->post('login', 'Auth\LoginController@login');
$this->post('logout', 'Auth\LoginController@logout')->name('logout');
// Registration Routes...
$this->get('register', 'Auth\RegisterController@showRegistrationForm')->name('register');
$this->post('register', 'Auth\RegisterController@register');
// Password Reset Routes...
}
由路由信息可知,注册、登录对应的控制器和方法。
- 限制认证用户才能访问
通常我们有一些接口是需要登录后才能访问的,在 Laravel 中是给路由分组,并添加中间件来实现。
// 请求到达路由中的 Controller 前,会先通过 auth 中间件验证
Route::middleware('auth')->group(function () {
Route::get('/user', 'UserController@index');
// ....
});
在 App\Http\Kernel
中定义了 auth
中间件
protected $routeMiddleware = [
'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
// ....
]
注册、登录、登录检测逻辑
注册逻辑
注册逻辑在控制器 Auth\RegisterController
的 register
方法中,具体是由控制器中的 RegistersUsers
trait 类实现。
public function register(Request $request)
{
// 调用控制器中的方法对参数进行验证,包括 name\email\password
$this->validator($request->all())->validate();
// 调用控制器中 create 方法创建用户
// 使用 $user 初始化 Redistered 对象
// 通过 event() 函数触发注册事件
event(new Registered($user = $this->create($request->all())));
// 获取 guard 实例,并通过 guard 登录创建的用户
$this->guard()->login($user);
// 根据 $user 注册信息进行页面跳转
return $this->registered($request, $user)
?: redirect($this->redirectPath());
}
protected function guard()
{
// 通过 Illuminate\Support\Facades\Auth 获取对应 guard 对象
return Auth::guard();
}
登录逻辑
登录逻辑在控制器 Auth\LoginController
的 login
方法中,具体是由控制器中的 AuthenticatesUsers
trait 类实现。
public function login(Request $request)
{
// 参数验证, email\password 参数
$this->validateLogin($request);
// 使用 ThrottlesLogins trait 对登录进行限制(一分钟内,登录失败超过配置的次数,将不能进行登录),防止恶意的登录尝试
// 限制的依据是 登录的 email 和 IP 地址。
if ($this->hasTooManyLoginAttempts($request)) {
// 触发登录锁定事件
$this->fireLockoutEvent($request);
// 返回锁定的响应信息
return $this->sendLockoutResponse($request);
}
// 根据 $request 中的登录凭证尝试登录
// 这里实际以是获取 guard 对象进行登录尝试
if ($this->attemptLogin($request)) {
// 登录成功后,重新生成 session,并跳转到设置的登录成功页面
return $this->sendLoginResponse($request);
}
// 没登录成功,增加登录失败次数,返回登录失败的响应
$this->incrementLoginAttempts($request);
return $this->sendFailedLoginResponse($request);
}
protected function attemptLogin(Request $request)
{
// 通过调用 Illuminate\Support\Facades\Auth 获取 guard 对象,并通过 guard 进行实际的登录逻辑
return $this->guard()->attempt(
$this->credentials($request), $request->has('remember')
);
}
登录检测逻辑
中间件中通过 authenticate
方法检测用户是否登录
protected function authenticate(array $guards)
{
// 默认时,传递的 $guards 为空
if (empty($guards)) {
// 调用注入的 auth 对象的 authenticate 方法。auth 会调用默认 guard 的 authenticate 方法
return $this->auth->authenticate();
}
foreach ($guards as $guard) {
// 调用特定 guard 的 check 方法
if ($this->auth->guard($guard)->check()) {
return $this->auth->shouldUse($guard);
}
}
throw new AuthenticationException('Unauthenticated.', $guards);
}
认证底层实现源码阅读
上面 Controller 中的登录和注册逻辑最终都是调用 Illuminate\Support\Facades\Auth
来获取 guard
,并通过 guard
来进行实际的登录、注册逻辑。
Illuminate\Support\Facades\Auth
是应用为 Illuminate\Auth\AuthManager
类提供的一个静态的接口,所以应用最终是使用 AuthManager
中的 guard()
方法来获取 guard
实例的。Facade
的原理可以参考文档。
下面为获取 guard
实例的主要逻辑:
public function guard($name = null)
{
// 如果没有传 $name,就获取默认的 guard 。 默认的为配置中的 web
$name = $name ?: $this->getDefaultDriver();
// 如果 guards 已经被实例化的,就直接调用,否则通过 resolve 方法创建 guard 对象
return isset($this->guards[$name])
? $this->guards[$name]
: $this->guards[$name] = $this->resolve($name);
}
protected function resolve($name)
{
// 获取名称对应的 guard 配置,前面说了,包括 driver\provider
$config = $this->getConfig($name);
// 配置异常判断
if (is_null($config)) {
throw new InvalidArgumentException("Auth guard [{$name}] is not defined.");
}
// 如果有设置自定义的 guard 创建方法,则用自定义的方法创建
// 自定义的方法通过类中的 extend 方法定义
if (isset($this->customCreators[$config['driver']])) {
return $this->callCustomCreator($name, $config);
}
$driverMethod = 'create'.ucfirst($config['driver']).'Driver';
// 根据配置,判断是否有对应创建 guard 的方法,有则调用
// 系统自带 createSessionDriver\createTokenDriver 两个创建 guard 方法
if (method_exists($this, $driverMethod)) {
return $this->{$driverMethod}($name, $config);
}
// 抛出 guard driver 没有定义的异常
throw new InvalidArgumentException("Auth guard driver [{$name}] is not defined.");
}
默认情况下,web
guard 的 driver
为 session。所以是通过 createSessionDriver
方法将会返回一个 SessionGuard
实例。然后通过 SessionGuard
实例进行用户认证逻辑。
这里最灵活的地方在于还提供了一个 extend
方法来自定义 guard。使得可以方便的扩展自己的用户认证。
下面是用户认证的相关的类图。
最后
有问题,欢迎留言交流。
参考文档
https://laravel-china.org/docs/laravel/5.4/authentication/1239