Laravel 源碼解讀系列第四篇-Auth 機制


前言

Laravel有一個神器:

php artisan make:auth

能夠快速的幫我們完成一套注冊和登錄的認證機制,但是這套機制具體的是怎么跑起來的呢?我們不妨來一起看看他的源碼。不過在這篇文章中,我只會闡述大致的流程,至於一些具體的細節,比如他的登錄次數限制是怎么完成的之類的不妨自己去尋找答案。
源碼解讀系列,有興趣的小伙伴可以點個star,我會持續更新各個部分的解讀,也是和大家一起進步的一個過程,如有寫的不對的地方還望指出。

過程

路由

當我們執行完命令之后,我們會發現,在routes/web.php中多了這樣一行代碼:

Auth::routes();

結合我們在前面講到的Facades,我們會執行Facades/Auth.phproutes方法:

public static function routes()
    {
        static::$app->make('router')->auth();
    }

$app->make('router')會返回一個Routing/Router.php對象的實例,而他的auth方法是:

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...
        $this->get('password/reset', 'Auth\ForgotPasswordController@showLinkRequestForm')->name('password.request');
        $this->post('password/email', 'Auth\ForgotPasswordController@sendResetLinkEmail')->name('password.email');
        $this->get('password/reset/{token}', 'Auth\ResetPasswordController@showResetForm')->name('password.reset');
        $this->post('password/reset', 'Auth\ResetPasswordController@reset');
    }

而這里的getpost方法,其實也就是我們通過Route::get等最終會調用的方法。

注冊

我們直接看表單提交的方法:

        $this->post('register', 'Auth\RegisterController@register');
//RegisterController.php
class RegisterController extends Controller
{
    /*
    |--------------------------------------------------------------------------
    | Register Controller
    |--------------------------------------------------------------------------
    |
    | This controller handles the registration of new users as well as their
    | validation and creation. By default this controller uses a trait to
    | provide this functionality without requiring any additional code.
    |
    */

    use RegistersUsers;

    /**
     * Where to redirect users after registration.
     *
     * @var string
     */
    protected $redirectTo = '/home';

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('guest');
    }

    /**
     * Get a validator for an incoming registration request.
     *
     * @param  array  $data
     * @return \Illuminate\Contracts\Validation\Validator
     */
    protected function validator(array $data)
    {
        return Validator::make($data, [
            'name' => 'required|string|max:255',
            'email' => 'required|string|email|max:255|unique:users',
            'password' => 'required|string|min:6|confirmed',
        ]);
    }

    /**
     * Create a new user instance after a valid registration.
     *
     * @param  array  $data
     * @return User
     */
    protected function create(array $data)
    {
        return User::create([
            'name' => $data['name'],
            'email' => $data['email'],
            'password' => bcrypt($data['password']),
        ]);
    }
}

里面使用到了RegistersUsers這個trait,里面就有我們需要的register方法:

public function register(Request $request)
    {
        $this->validator($request->all())->validate();

        event(new Registered($user = $this->create($request->all())));

//        默認情況下,$this->guard()會返回一個`SessionGuard`的對象
        $this->guard()->login($user);

        return $this->registered($request, $user)
                        ?: redirect($this->redirectPath());
    }

    protected function guard()
    {
        return Auth::guard();
    }

其中最核心的部分就是:

$this->guard()->login($user);

$this->guard()在默認配置下會返回一個SessionGuard的對象,具體是這樣實現的:

前面我們有講過,當我們執行Facades下一個不存在的方法時,我們會調用Facade.php__callStatic方法,這個方法會獲取當前對象的getFacadeAccessor方法而返回一個對應的對象並調用他所需要調用的方法,這個詳細過程在我的第三篇中都有所闡述。
auth的別名綁定,是在我們初始化的過程中綁定的,具體可以看我寫的第一篇,所以,這里我們會調用AuthManager這個對象:

 public function guard($name = null)
    {
        $name = $name ?: $this->getDefaultDriver();

        return isset($this->guards[$name])
                    ? $this->guards[$name]
                    : $this->guards[$name] = $this->resolve($name);
    }

public function getDefaultDriver()
    {
        return $this->app['config']['auth.defaults.guard'];
    }

protected function resolve($name)
    {
        $config = $this->getConfig($name);

        if (is_null($config)) {
            throw new InvalidArgumentException("Auth guard [{$name}] is not defined.");
        }

        if (isset($this->customCreators[$config['driver']])) {
            return $this->callCustomCreator($name, $config);
        }

        $driverMethod = 'create'.ucfirst($config['driver']).'Driver';

        if (method_exists($this, $driverMethod)) {
            return $this->{$driverMethod}($name, $config);
        }

        throw new InvalidArgumentException("Auth guard driver [{$name}] is not defined.");
    }
//config/auth.php

'defaults' => [
        'guard' => 'web',
        'passwords' => 'users',
    ],

    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],

        'api' => [
            'driver' => 'token',
            'provider' => 'users',
        ],
    ],

通過調用AuthManager可以拼接得出,最終他會調用一個createSessionDriver方法:

public function createSessionDriver($name, $config)
    {
        $provider = $this->createUserProvider($config['provider']);

        $guard = new SessionGuard($name, $provider, $this->app['session.store']);

        if (method_exists($guard, 'setCookieJar')) {
            $guard->setCookieJar($this->app['cookie']);
        }

        if (method_exists($guard, 'setDispatcher')) {
            $guard->setDispatcher($this->app['events']);
        }

        if (method_exists($guard, 'setRequest')) {
            $guard->setRequest($this->app->refresh('request', $guard, 'setRequest'));
        }

        return $guard;
    }

所以最終是調用了SessionGuardlogin方法來完成session部分的功能。

登錄

展示表單的部分就不贅述了,這里我們直接看:

$this->post('login', 'Auth\LoginController@login');
//LoginController.php
class LoginController extends Controller
{
    use AuthenticatesUsers;

    /**
     * Where to redirect users after login.
     *
     * @var string
     */
    protected $redirectTo = '/home';

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('guest')->except('logout');
    }
}

他使用到了一個AuthenticatesUserstrait,里面有一個login方法,也就是我們要使用到的login了:

public function login(Request $request)
    {
        $this->validateLogin($request);

        if ($this->hasTooManyLoginAttempts($request)) {
            $this->fireLockoutEvent($request);

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

        if ($this->attemptLogin($request)) {
            return $this->sendLoginResponse($request);
        }

        $this->incrementLoginAttempts($request);

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

其實大部分和register的部分差不多,核心部分還是在於SessionGuard對象的獲取,這里就不過多贅述了,不過值得一提的是,像Auth::check()等很多方法的使用,其實也會通過SessionGuard來完成,主要是通過:

public function __call($method, $parameters)
    {
        return $this->guard()->{$method}(...$parameters);
    }
public function check()
    {
        return ! is_null($this->user());
    }

來得以完成調度。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM