Laravel 用戶認(rèn)證 源碼學(xué)習(xí)

本文介紹 Laravel 用戶認(rèn)證的使用、基本邏輯和底層的實(shí)現(xiàn)。

快速使用

在 Laravel 框架初始化后,運(yùn)行 php artisan make:authphp artisan migrate 就能啟用 Laravel 自帶的用戶認(rèn)證功能。

  1. 用戶認(rèn)證的配置文件在 config/auth.php 中,
// 默認(rèn)使用的配置。
// guard 是用戶認(rèn)證邏輯的實(shí)現(xiàn)。web 是網(wǎng)站用戶認(rèn)證邏輯;api 是 API 用戶認(rèn)證邏輯
'defaults' => [
        'guard' => 'web',
        'passwords' => 'users',
],

guard 還需配置對(duì)應(yīng)的 driverprovider。
driver 配置對(duì)應(yīng)具體實(shí)現(xiàn) guard 的邏輯類;provider 是邏輯類使用的數(shù)據(jù)提供者(查詢、更新對(duì)應(yīng)的用戶信息的具體實(shí)現(xiàn)類)。

'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
        ...
    ],
  1. 認(rèn)證的路由信息在 Illuminate\Routing\Router.php 中,通過 Illuminate\Support\Facades\Authroutes() 方法引入。注冊和登錄部分如下:
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...
 }

由路由信息可知,注冊、登錄對(duì)應(yīng)的控制器和方法。

  1. 限制認(rèn)證用戶才能訪問
    通常我們有一些接口是需要登錄后才能訪問的,在 Laravel 中是給路由分組,并添加中間件來實(shí)現(xiàn)。
// 請求到達(dá)路由中的 Controller 前,會(huì)先通過 auth 中間件驗(yàn)證
Route::middleware('auth')->group(function () {
    Route::get('/user', 'UserController@index');
    // ....
});

App\Http\Kernel 中定義了 auth 中間件

 protected $routeMiddleware = [
        'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
        // ....
]

注冊、登錄、登錄檢測邏輯

注冊邏輯

注冊邏輯在控制器 Auth\RegisterControllerregister 方法中,具體是由控制器中的 RegistersUsers trait 類實(shí)現(xiàn)。

public function register(Request $request)
{
     // 調(diào)用控制器中的方法對(duì)參數(shù)進(jìn)行驗(yàn)證,包括 name\email\password
     $this->validator($request->all())->validate();
     // 調(diào)用控制器中 create 方法創(chuàng)建用戶
     // 使用 $user 初始化 Redistered 對(duì)象
     // 通過 event() 函數(shù)觸發(fā)注冊事件
     event(new Registered($user = $this->create($request->all())));
      
     // 獲取 guard 實(shí)例,并通過 guard 登錄創(chuàng)建的用戶
     $this->guard()->login($user);
     // 根據(jù) $user 注冊信息進(jìn)行頁面跳轉(zhuǎn)
     return $this->registered($request, $user)
                        ?: redirect($this->redirectPath());
}

protected function guard()
{
    // 通過 Illuminate\Support\Facades\Auth 獲取對(duì)應(yīng) guard 對(duì)象
    return Auth::guard();
}

登錄邏輯

登錄邏輯在控制器 Auth\LoginControllerlogin 方法中,具體是由控制器中的 AuthenticatesUsers trait 類實(shí)現(xiàn)。

public function login(Request $request)
{
   // 參數(shù)驗(yàn)證, email\password 參數(shù)
   $this->validateLogin($request);

   // 使用 ThrottlesLogins trait 對(duì)登錄進(jìn)行限制(一分鐘內(nèi),登錄失敗超過配置的次數(shù),將不能進(jìn)行登錄),防止惡意的登錄嘗試
   // 限制的依據(jù)是 登錄的 email 和 IP 地址。
  
   if ($this->hasTooManyLoginAttempts($request)) {
        // 觸發(fā)登錄鎖定事件
        $this->fireLockoutEvent($request);
        // 返回鎖定的響應(yīng)信息
        return $this->sendLockoutResponse($request);
   }
   // 根據(jù) $request 中的登錄憑證嘗試登錄
   // 這里實(shí)際以是獲取 guard 對(duì)象進(jìn)行登錄嘗試 
   if ($this->attemptLogin($request)) {
       // 登錄成功后,重新生成 session,并跳轉(zhuǎn)到設(shè)置的登錄成功頁面
       return $this->sendLoginResponse($request);
   }

   // 沒登錄成功,增加登錄失敗次數(shù),返回登錄失敗的響應(yīng)
   $this->incrementLoginAttempts($request);

   return $this->sendFailedLoginResponse($request);
}

protected function attemptLogin(Request $request)
{
    // 通過調(diào)用 Illuminate\Support\Facades\Auth 獲取 guard 對(duì)象,并通過 guard 進(jìn)行實(shí)際的登錄邏輯
    return $this->guard()->attempt(
       $this->credentials($request), $request->has('remember')
    );
}

登錄檢測邏輯

中間件中通過 authenticate 方法檢測用戶是否登錄

protected function authenticate(array $guards)
{  
    // 默認(rèn)時(shí),傳遞的 $guards 為空
    if (empty($guards)) {
        // 調(diào)用注入的 auth 對(duì)象的 authenticate 方法。auth 會(huì)調(diào)用默認(rèn) guard 的 authenticate 方法
        return $this->auth->authenticate();
    }

    foreach ($guards as $guard) {
        // 調(diào)用特定 guard 的 check 方法
        if ($this->auth->guard($guard)->check()) {
            return $this->auth->shouldUse($guard);
        }
    }

    throw new AuthenticationException('Unauthenticated.', $guards);
}

認(rèn)證底層實(shí)現(xiàn)源碼閱讀

上面 Controller 中的登錄和注冊邏輯最終都是調(diào)用 Illuminate\Support\Facades\Auth 來獲取 guard ,并通過 guard 來進(jìn)行實(shí)際的登錄、注冊邏輯。
Illuminate\Support\Facades\Auth 是應(yīng)用為 Illuminate\Auth\AuthManager 類提供的一個(gè)靜態(tài)的接口,所以應(yīng)用最終是使用 AuthManager 中的 guard() 方法來獲取 guard 實(shí)例的。Facade 的原理可以參考文檔。
下面為獲取 guard 實(shí)例的主要邏輯:

public function guard($name = null)
{
    // 如果沒有傳 $name,就獲取默認(rèn)的 guard 。 默認(rèn)的為配置中的 web
    $name = $name ?: $this->getDefaultDriver();
    // 如果 guards 已經(jīng)被實(shí)例化的,就直接調(diào)用,否則通過 resolve 方法創(chuàng)建 guard 對(duì)象
    return isset($this->guards[$name])
                ? $this->guards[$name]
                : $this->guards[$name] = $this->resolve($name);
}

protected function resolve($name)
{
    // 獲取名稱對(duì)應(yīng)的 guard 配置,前面說了,包括 driver\provider
    $config = $this->getConfig($name);
    // 配置異常判斷
    if (is_null($config)) {
        throw new InvalidArgumentException("Auth guard [{$name}] is not defined.");
    }
    // 如果有設(shè)置自定義的 guard 創(chuàng)建方法,則用自定義的方法創(chuàng)建
    // 自定義的方法通過類中的 extend 方法定義
    if (isset($this->customCreators[$config['driver']])) {
        return $this->callCustomCreator($name, $config);
    }

    $driverMethod = 'create'.ucfirst($config['driver']).'Driver';
    // 根據(jù)配置,判斷是否有對(duì)應(yīng)創(chuàng)建 guard 的方法,有則調(diào)用
    // 系統(tǒng)自帶 createSessionDriver\createTokenDriver 兩個(gè)創(chuàng)建 guard 方法 
    if (method_exists($this, $driverMethod)) {
        return $this->{$driverMethod}($name, $config);
    }
    // 拋出 guard driver 沒有定義的異常
    throw new InvalidArgumentException("Auth guard driver [{$name}] is not defined.");
}

默認(rèn)情況下,web guard 的 driver 為 session。所以是通過 createSessionDriver 方法將會(huì)返回一個(gè) SessionGuard 實(shí)例。然后通過 SessionGuard 實(shí)例進(jìn)行用戶認(rèn)證邏輯。
這里最靈活的地方在于還提供了一個(gè) extend 方法來自定義 guard。使得可以方便的擴(kuò)展自己的用戶認(rèn)證。
下面是用戶認(rèn)證的相關(guān)的類圖。

用戶認(rèn)證類圖.png

最后

有問題,歡迎留言交流。

參考文檔

https://laravel-china.org/docs/laravel/5.4/authentication/1239

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容