Laravel 使用者認證體系詳解
阿新 • • 發佈:2018-11-20
本篇文章基於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\[email protected]')->name('login');
$this->post('login', 'Auth\[email protected]');
$this->post ('logout', 'Auth\[email protected]')->name('logout');
// Registration Routes...
$this->get('register', 'Auth\[email protected]');
$this->post('register', 'Auth\[email protected]');
// Password Reset Routes...
$this->get('password/reset', 'Auth\[email protected] ');
$this->post('password/email', 'Auth\[email protected]');
$this->get('password/reset/{token}', 'Auth\[email protected]');
$this->post('password/reset', 'Auth\[email protected]');
}
本文我們主要來說說登入流程:
/**
* 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;
}