Laravel 用户认证体系详解
本篇文章基于laravel 5.3。
用户注册、登录及密码找回等功能几乎是web系统的标配。正因如此,laravel将其作为一个独立的部分抽象出来,供开发者使用,极大提高了开发人员的效率。
简单来说,用户认证就是系统对用户提供的登录信息进行校验的过程。这一过程可以抽象为如下几个部分:
- 用户如何提供登录信息,如何表示?
- 系统如何校验登录信息?
- 系统如何维护登录成功后的认证信息?
laravel提供了一套完整的用户认证体系,开箱即用。只需要运行如下命令即可:
php artisan make:auth
通过调用Auth::routes()
方法,laravel为用户认证体系注册了以下路由:
/**
* Register the typical authentication routes for an application.
*
* @return void
*/
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');
$this->post('register', 'Auth\RegisterController@register');
// Password Reset Routes...
$this->get('password/reset', 'Auth\ForgotPasswordController@showLinkRequestForm');
$this->post('password/email', 'Auth\ForgotPasswordController@sendResetLinkEmail');
$this->get('password/reset/{token}', 'Auth\ResetPasswordController@showResetForm');
$this->post('password/reset', 'Auth\ResetPasswordController@reset');
}
本文我们主要来说说登录流程:
/**
* Handle a login request to the application.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function login(Request $request)
{
// 参数合法性校验,校验规则:[$this->username() => 'required', 'password' => 'required',]
// 此处用户名字段名是通过username()方法获取的,默认是email,方便开发者自定义字段名称
$this->validateLogin($request);
// If the class is using the ThrottlesLogins trait, we can automatically throttle
// the login attempts for this application. We'll key this by the username and
// the IP address of the client making these requests into this application.
// 登录尝试次数限制,具体由 Illuminate\Cache\RateLimiter 类实现相关功能
if ($this->hasTooManyLoginAttempts($request)) {
$this->fireLockoutEvent($request);
return $this->sendLockoutResponse($request);
}
// 参数过滤,只保留用户名和密码字段
$credentials = $this->credentials($request);
/*验证登录信息有效性。此处laravel将具体的认证工作分离,以不同的认证驱动(由Illuminate\Auth\AuthManager类的guard方法获取,默认为web,在config/auth.php中配置)来实现相关逻辑。
在具体的认证驱动中,attempt方法首先根据认证信息获取相应的用户,然后根据认证信息和获取的用户来校验用户的合法性,最终实现登录用户的合法性校验。
此处看似简单的登录认证,laravel为了可扩展性,其实现有点绕,涉及的类比较多,就连密码hash部分也独立为单独的类(Illuminate\Hashing\BcryptHasher,实现了Illuminate\Contracts\Hashing\Hasher接口),基本上整个认证过程可变的部分都做了基于接口的编程实现。在config/auth.php配置文件中注释部分有比较详细的说明,可以结合仔细阅读下。
*/
if ($this->guard()->attempt($credentials, $request->has('remember'))) {
return $this->sendLoginResponse($request);
}
// If the login attempt was unsuccessful we will increment the number of attempts
// to login and redirect the user back to the login form. Of course, when this
// user surpasses their maximum number of attempts they will get locked out.
$this->incrementLoginAttempts($request);
return $this->sendFailedLoginResponse($request);
}
/**
* Attempt to authenticate a user using the given credentials.
*
* @param array $credentials
* @param bool $remember
* @param bool $login
* @return bool
*/
public function attempt(array $credentials = [], $remember = false, $login = true)
{
$this->fireAttemptEvent($credentials, $remember, $login);
$this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials);
// If an implementation of UserInterface was returned, we'll ask the provider
// to validate the user against the given credentials, and if they are in
// fact valid we'll log the users into the application and return true.
if ($this->hasValidCredentials($user, $credentials)) {
if ($login) {
$this->login($user, $remember);
}
return true;
}
// If the authentication attempt fails we will fire an event so that the user
// may be notified of any suspicious attempts to access their account from
// an unrecognized user. A developer may listen to this event as needed.
if ($login) {
$this->fireFailedEvent($user, $credentials);
}
return false;
}
这里我们多聊下认证驱动。不管何种认证驱动,按照laravel的说法都需要一个User Provider
,这个User Provider
有什么用呢?简单说就是实现用户信息的保存及获取。laravel定义了一个Illuminate\Contracts\Auth\UserProvider
接口,所有User Provider
都需要实现这个接口。另外还有一点是laravel对用户进行了抽象,定义了一个Illuminate\Contracts\Auth\Authenticatable
接口,上面说的用户都是实现了该接口的类的实例对象。
interface UserProvider
{
/**
* Retrieve a user by their unique identifier.
*
* @param mixed $identifier
* @return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function retrieveById($identifier);
/**
* 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($identifier, $token);
/**
* 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);
/**
* Retrieve a user by the given credentials.
*
* @param array $credentials
* @return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function retrieveByCredentials(array $credentials);
/**
* 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);
}
laravel提供了两个User Provider
:EloquentUserProvider
和DatabaseUserProvider
。两者的区别在于前者是基于laravel的Eloquent与数据库打交道,后者是更底层些,直接与数据库打交道。看看二者的构造函数便可知一二:
/**
* Create a new Eloquent user provider.
*
* @param \Illuminate\Contracts\Hashing\Hasher $hasher
* @param string $model The Eloquent user model
* @return void
*/
public function __construct(HasherContract $hasher, $model)
{
$this->model = $model;
$this->hasher = $hasher;
}
/**
* Create a new database user provider.
*
* @param \Illuminate\Database\ConnectionInterface $conn
* @param \Illuminate\Contracts\Hashing\Hasher $hasher
* @param string $table
* @return void
*/
public function __construct(ConnectionInterface $conn, HasherContract $hasher, $table)
{
$this->conn = $conn;
$this->table = $table;
$this->hasher = $hasher;
}
以上,完。
本作品采用《CC 协议》,转载必须注明作者和本文链接
不友善信息已经删除,下次遇到这种情况可以 @ 管理员过来处理