zoukankan      html  css  js  c++  java
  • laravel5.5用户认证源码分析

    1. 生成相关文件和配置

    快速生成命令

    php artisan make:auth
    

    运行后,使用git查看有哪些文件变化

    $ git status
    # On branch master
    # Changed but not updated:
    #   (use "git add <file>..." to update what will be committed)
    #   (use "git checkout -- <file>..." to discard changes in working directory)
    #
    #	modified:   routes/web.php
    #
    # Untracked files:
    #   (use "git add <file>..." to include in what will be committed)
    #
    #	app/Http/Controllers/HomeController.php
    #	resources/views/auth/
    #	resources/views/home.blade.php
    #	resources/views/layouts/
    no changes added to commit (use "git add" and/or "git commit -a")
    

    2. 分析路由文件

    查看下路由文件,发现多了两条路由信息

    <?php
    
    /*
    |--------------------------------------------------------------------------
    | Web Routes
    |--------------------------------------------------------------------------
    |
    | Here is where you can register web routes for your application. These
    | routes are loaded by the RouteServiceProvider within a group which
    | contains the "web" middleware group. Now create something great!
    |
    */
    
    Route::get('/', function () {
        return view('welcome');
    });
    
    //以下为新加的路由
    Auth::routes();
    
    Route::get('/home', 'HomeController@index')->name('home');
    
    

    分析下Auth::routes()做了什么事

    定位到Auth这个Facades,config/app.php中的aliases中有一条

    'aliases' => [
            'Auth' => IlluminateSupportFacadesAuth::class,
            // ...其他
        ],
    

    打开IlluminateSupportFacadesAuth.php后看到有个routes()方法

        /**
         * Register the typical authentication routes for an application.
         *
         * @return void
         */
        public static function routes()
        {
            static::$app->make('router')->auth();
        }
    

    定位到IlluminateRouting oute.php,有个auth()方法,这个就是我们routeweb.php路由文件中Autu::routes()最后能生成的路由。

        public function auth()
        {
            // Authentication Routes...
            $this->get('login', 'AuthLoginController@showLoginForm')->name('login');
            $this->post('login', 'AuthLoginController@login');
            $this->post('logout', 'AuthLoginController@logout')->name('logout');
    
            // Registration Routes...
            $this->get('register', 'AuthRegisterController@showRegistrationForm')->name('register');
            $this->post('register', 'AuthRegisterController@register');
    
            // Password Reset Routes...
            $this->get('password/reset', 'AuthForgotPasswordController@showLinkRequestForm')->name('password.request');
            $this->post('password/email', 'AuthForgotPasswordController@sendResetLinkEmail')->name('password.email');
            $this->get('password/reset/{token}', 'AuthResetPasswordController@showResetForm')->name('password.reset');
            $this->post('password/reset', 'AuthResetPasswordController@reset');
        }
    

    查看下生效的路由,和上面的对比下,相匹配

    $ php artisan route:list
    +--------+----------+------------------------+------------------+------------------------------------------------------------------------+--------------+
    | Domain | Method   | URI                    | Name             | Action                                                                 | Middleware   |
    +--------+----------+------------------------+------------------+------------------------------------------------------------------------+--------------+
    |        | GET|HEAD | /                      |                  | Closure                                                                | web          |
    |        | GET|HEAD | api/user               |                  | Closure                                                                | api,auth:api |
    |        | GET|HEAD | home                   | home             | AppHttpControllersHomeController@index                              | web,auth     |
    |        | GET|HEAD | login                  | login            | AppHttpControllersAuthLoginController@showLoginForm                | web,guest    |
    |        | POST     | login                  |                  | AppHttpControllersAuthLoginController@login                        | web,guest    |
    |        | POST     | logout                 | logout           | AppHttpControllersAuthLoginController@logout                       | web          |
    |        | POST     | password/email         | password.email   | AppHttpControllersAuthForgotPasswordController@sendResetLinkEmail  | web,guest    |
    |        | GET|HEAD | password/reset         | password.request | AppHttpControllersAuthForgotPasswordController@showLinkRequestForm | web,guest    |
    |        | POST     | password/reset         |                  | AppHttpControllersAuthResetPasswordController@reset                | web,guest    |
    |        | GET|HEAD | password/reset/{token} | password.reset   | AppHttpControllersAuthResetPasswordController@showResetForm        | web,guest    |
    |        | GET|HEAD | register               | register         | AppHttpControllersAuthRegisterController@showRegistrationForm      | web,guest    |
    |        | POST     | register               |                  | AppHttpControllersAuthRegisterController@register                  | web,guest    |
    +--------+----------+------------------------+------------------+------------------------------------------------------------------------+--------------+
    
    

    3. 以登陆开始为例,分析auth到底是怎么工作的

    3.1 分析登录文件

    定位文件 app/Http/Controllers/Auth/LoginController.php

    <?php
    
    namespace AppHttpControllersAuth;
    
    use AppHttpControllersController;
    use IlluminateFoundationAuthAuthenticatesUsers;
    
    # 1. 登录控制器
    class LoginController extends Controller
    {
        /*
        |--------------------------------------------------------------------------
        | Login Controller
        |--------------------------------------------------------------------------
        |
        | This controller handles authenticating users for the application and
        | redirecting them to your home screen. The controller uses a trait
        | to conveniently provide its functionality to your applications.
        |
        */
    
        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');
        }
    }
    

    LoginController.php并没有登录方法,why?实际上这里使用了trait,如果你用过软删除应该记得

    use SoftDeletes;
    

    这里也是使用了trait

    use AuthenticatesUsers;
    

    打开IlluminateFoundationAuthAuthenticatesUsers.php

    <?php
    
    namespace IlluminateFoundationAuth;
    
    use IlluminateHttpRequest;
    use IlluminateSupportFacadesAuth;
    use IlluminateValidationValidationException;
    
    trait AuthenticatesUsers
    {
        use RedirectsUsers, ThrottlesLogins;
    
    
        /**
         * Handle a login request to the application.
         *
         * @param  IlluminateHttpRequest  $request
         * @return IlluminateHttpRedirectResponse|IlluminateHttpResponse|IlluminateHttpJsonResponse
         */
        public function login(Request $request)
        {
            $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.
            if ($this->hasTooManyLoginAttempts($request)) {
                $this->fireLockoutEvent($request);
    
                return $this->sendLockoutResponse($request);
            }
    
            # 2. 这里调用了attemptLogin()
            if ($this->attemptLogin($request)) {
                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 log the user into the application.
         *
         * @param  IlluminateHttpRequest  $request
         * @return bool
         */
        protected function attemptLogin(Request $request)
        {   
            # 3. 找到 guard()方法
            # 这里的attemp()方法一会分析
            return $this->guard()->attempt(
                $this->credentials($request), $request->filled('remember')
            );
        }
        
        /**
         * Get the guard to be used during authentication.
         *
         * @return IlluminateContractsAuthStatefulGuard
         */
        protected function guard()
        {
            # 4. 访问了Auth门面的guard()方法
            return Auth::guard();
        }
        
        // ...其它方法暂时省略
    
    }
    
    

    到这里我们知道login方法的结果 : 先实例化Auth::guard()实例,最终返回了这个实例的attempt()方法

    3.2 分析门面Auth。

    以下使用config/auth.php的原始默认配置进行分析

    根据config/app.php里面的服务提供器和Facades, 最终定位到 IlluminateAuthAuthManager.php

    
        /**
         * Attempt to get the guard from the local cache.
         *
         * @param  string  $name
         * @return IlluminateContractsAuthGuard|IlluminateContractsAuthStatefulGuard
         */
        public function guard($name = null)
        {
            # 5. 获取看守器,如果guard()没有参数则使用默认设置
            $name = $name ?: $this->getDefaultDriver();
    
            # 7. $name= 'web' 定位到resolve()方法
            return $this->guards[$name] ?? $this->guards[$name] = $this->resolve($name);
        }
    
        /**
         * Resolve the given guard.
         *
         * @param  string  $name
         * @return IlluminateContractsAuthGuard|IlluminateContractsAuthStatefulGuard
         *
         * @throws InvalidArgumentException
         */
        protected function resolve($name)
        {
            #8. 获取配置
            $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);
            }
    
            # 10 .$config = ['driver' => 'session','provider' => 'users',]
            # 拼接创建看守器的方法名  这里默认是 createSessionDriver
            $driverMethod = 'create'.ucfirst($config['driver']).'Driver';
    
            if (method_exists($this, $driverMethod)) {
                # 11. 访问createSessionDriver()
                return $this->{$driverMethod}($name, $config);
            }
    
            throw new InvalidArgumentException("Auth guard driver [{$name}] is not defined.");
        }
    
        /**
         * Call a custom driver creator.
         *
         * @param  string  $name
         * @param  array  $config
         * @return mixed
         */
        protected function callCustomCreator($name, array $config)
        {
            return $this->customCreators[$config['driver']]($this->app, $name, $config);
        }
    
        /**
         * Create a session based authentication guard.
         *
         * @param  string  $name
         * @param  array  $config
         * @return IlluminateAuthSessionGuard
         */
        public function createSessionDriver($name, $config)
        {
            # $name = 'session'
            # $cofnig['provider'] = 'users'
            
            # 12. createUserProvider不在此文件,定位到最上面有使用trait
            # 通过这句 use CreatesUserProviders; 定位
            # 查看下面的IlluminateAuthCreatesUserProviders.php文件
            $provider = $this->createUserProvider($config['provider'] ?? null);
    
    
            $guard = new SessionGuard($name, $provider, $this->app['session.store']);
    
            # 18. 到这里我们知道
            # $provide 是EloquentUserProvider实例
            # $guard 是 SessionGuard实例 
            # 在回头看第3步,Auth::guard()->attempt()
            # 接下来看下面IlluminateAuthSessionGuard.php中的attempt()
            
            // When using the remember me functionality of the authentication services we
            // will need to be set the encryption instance of the guard, which allows
            // secure, encrypted cookie values to get generated for those cookies.
            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;
        }
    
        /**
         * Create a token based authentication guard.
         *
         * @param  string  $name
         * @param  array  $config
         * @return IlluminateAuthTokenGuard
         */
        public function createTokenDriver($name, $config)
        {
            // The token guard implements a basic API token based guard implementation
            // that takes an API token field from the request and matches it to the
            // user in the database or another persistence layer where users are.
            $guard = new TokenGuard(
                $this->createUserProvider($config['provider'] ?? null),
                $this->app['request']
            );
    
            $this->app->refresh('request', $guard, 'setRequest');
    
            return $guard;
        }
    
        /**
         * Get the guard configuration.
         *
         * @param  string  $name
         * @return array
         */
        protected function getConfig($name)
        {
            # 9. $name = 'web'
            # 读取config/auth.php 中的guards数组中的{$name}值,
            # 对应 'web' => ['driver' => 'session','provider' => 'users',],
            return $this->app['config']["auth.guards.{$name}"];
        }
    
        /**
         * Get the default authentication driver name.
         *
         * @return string
         */
        public function getDefaultDriver()
        {
            # 6. 读取config/auth.php 中的default数组中的guard值,默认是web
            return $this->app['config']['auth.defaults.guard'];
        }
    
        /**
         * Set the default guard driver the factory should serve.
         *
         * @param  string  $name
         * @return void
         */
        public function shouldUse($name)
        {
            $name = $name ?: $this->getDefaultDriver();
    
            $this->setDefaultDriver($name);
    
            $this->userResolver = function ($name = null) {
                return $this->guard($name)->user();
            };
        }
    
        /**
         * Set the default authentication driver name.
         *
         * @param  string  $name
         * @return void
         */
        public function setDefaultDriver($name)
        {
            $this->app['config']['auth.defaults.guard'] = $name;
        }
    
        /**
         * Register a new callback based request guard.
         *
         * @param  string  $driver
         * @param  callable  $callback
         * @return $this
         */
        public function viaRequest($driver, callable $callback)
        {
            return $this->extend($driver, function () use ($callback) {
                $guard = new RequestGuard($callback, $this->app['request'], $this->createUserProvider());
    
                $this->app->refresh('request', $guard, 'setRequest');
    
                return $guard;
            });
        }
    
        /**
         * Get the user resolver callback.
         *
         * @return Closure
         */
        public function userResolver()
        {
            return $this->userResolver;
        }
    
        /**
         * Set the callback to be used to resolve users.
         *
         * @param  Closure  $userResolver
         * @return $this
         */
        public function resolveUsersUsing(Closure $userResolver)
        {
            $this->userResolver = $userResolver;
    
            return $this;
        }
    
        /**
         * Register a custom driver creator Closure.
         *
         * @param  string  $driver
         * @param  Closure  $callback
         * @return $this
         */
        public function extend($driver, Closure $callback)
        {
            $this->customCreators[$driver] = $callback;
    
            return $this;
        }
    
        /**
         * Register a custom provider creator Closure.
         *
         * @param  string  $name
         * @param  Closure  $callback
         * @return $this
         */
        public function provider($name, Closure $callback)
        {
            $this->customProviderCreators[$name] = $callback;
    
            return $this;
        }
    
        /**
         * Dynamically call the default driver instance.
         *
         * @param  string  $method
         * @param  array  $parameters
         * @return mixed
         */
        public function __call($method, $parameters)
        {
            return $this->guard()->{$method}(...$parameters);
        }
    }
    

    IlluminateAuthCreatesUserProviders.php

    <?php
    
    namespace IlluminateAuth;
    
    use InvalidArgumentException;
    
    trait CreatesUserProviders
    {
        /**
         * The registered custom provider creators.
         *
         * @var array
         */
        protected $customProviderCreators = [];
    
        /**
         * Create the user provider implementation for the driver.
         *
         * @param  string|null  $provider
         * @return IlluminateContractsAuthUserProvider|null
         *
         * @throws InvalidArgumentException
         */
        public function createUserProvider($provider = null)
        {
            # $provider = 'users'
            
            # 13. 获取提供器的配置
            if (is_null($config = $this->getProviderConfiguration($provider))) {
                return;
            }
    
            if (isset($this->customProviderCreators[$driver = ($config['driver'] ?? null)])) {
                return call_user_func(
                    $this->customProviderCreators[$driver], $this->app, $config
                );
            }
    
            # 15. $config = ['driver' => 'eloquent','model' => AppUser::class,]
            # $driver ='eloquent'
            switch ($driver) {
                case 'database':
                    return $this->createDatabaseProvider($config);
                case 'eloquent':
                    # 16. 调用createEloquentProvider方法
                    return $this->createEloquentProvider($config);
                default:
                    throw new InvalidArgumentException(
                        "Authentication user provider [{$driver}] is not defined."
                    );
            }
        }
    
        /**
         * Get the user provider configuration.
         *
         * @param  string|null  $provider
         * @return array|null
         */
        protected function getProviderConfiguration($provider)
        {
            # $provider = 'users'
            
            # 14. 读取config/auth.php 中的providers数组中的{$provider}值
            # 最终对应 'users' => ['driver' => 'eloquent','model' => AppUser::class,],
            if ($provider = $provider ?: $this->getDefaultUserProvider()) {
                return $this->app['config']['auth.providers.'.$provider];
            }
        }
    
        /**
         * Create an instance of the database user provider.
         *
         * @param  array  $config
         * @return IlluminateAuthDatabaseUserProvider
         */
        protected function createDatabaseProvider($config)
        {
            $connection = $this->app['db']->connection();
    
            return new DatabaseUserProvider($connection, $this->app['hash'], $config['table']);
        }
    
        /**
         * Create an instance of the Eloquent user provider.
         *
         * @param  array  $config
         * @return IlluminateAuthEloquentUserProvider
         */
        protected function createEloquentProvider($config)
        {
             # 17. 实例化EloquentUserProvider,
             # 记住提供器最终得到了EloquentUserProvider实例
             # 返回 12
            return new EloquentUserProvider($this->app['hash'], $config['model']);
        }
    
        /**
         * Get the default user provider name.
         *
         * @return string
         */
        public function getDefaultUserProvider()
        {
            return $this->app['config']['auth.defaults.provider'];
        }
    }
    

    IlluminateAuthSessionGuard.php 摘取主要涉及方法

        public function attempt(array $credentials = [], $remember = false)
        {
            $this->fireAttemptEvent($credentials, $remember);
    
            $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)) {
                # 19. 调用hasValidCredentials()
                # 22. 如果返回真 定位到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.
            $this->fireFailedEvent($user, $credentials);
    
            return false;
        }
        
        /**
         * Determine if the user matches the credentials.
         *
         * @param  mixed  $user
         * @param  array  $credentials
         * @return bool
         */
        protected function hasValidCredentials($user, $credentials)
        { 
            # 20. 我们已经分析过$this->provider,默认得到的是EloquentUserProvider实例
            # 查看下面文件 IlluminateAuthEloquentUserProvider.php
            return ! is_null($user) && $this->provider->validateCredentials($user, $credentials);
        }
        
        /**
         * Log a user into the application.
         *
         * @param  IlluminateContractsAuthAuthenticatable  $user
         * @param  bool  $remember
         * @return void
         */
        public function login(AuthenticatableContract $user, $remember = false)
        {
             # 23. 登录成功,存储session等操作。。。分析到此结束
            $this->updateSession($user->getAuthIdentifier());
    
            // If the user should be permanently "remembered" by the application we will
            // queue a permanent cookie that contains the encrypted copy of the user
            // identifier. We will then decrypt this later to retrieve the users.
            if ($remember) {
                $this->ensureRememberTokenIsSet($user);
    
                $this->queueRecallerCookie($user);
            }
    
            // If we have an event dispatcher instance set we will fire an event so that
            // any listeners will hook into the authentication events and run actions
            // based on the login and logout events fired from the guard instances.
            $this->fireLoginEvent($user, $remember);
    
            $this->setUser($user);
        }
    

    IlluminateAuthEloquentUserProvider.php 摘取主要涉及方法

        /**
         * Validate a user against the given credentials.
         *
         * @param  IlluminateContractsAuthAuthenticatable  $user
         * @param  array  $credentials
         * @return bool
         */
        public function validateCredentials(UserContract $user, array $credentials)
        {
            # 21. 最终这里进行了用户的验证,验证通过返回true,不通过返回false
            $plain = $credentials['password'];
    
            return $this->hasher->check($plain, $user->getAuthPassword());
        }
    

    以上只是简单的分析了login的大体流程,感兴趣大家可以按照这个思路继续分析其他源码,有机会希望多交流,互相进步

  • 相关阅读:
    软件工程之开发过程
    软件工程设计之四则运算
    Android笔记-5-EditText密码和Checkbox二选一
    Android笔记-4-实现登陆页面并跳转和简单的注册页面
    Android笔记-3-EditText的属性介绍
    Android笔记-2-TextView的属性详解
    Android笔记-1
    Microsoft Build 2015
    网络受限是个什么东东?
    几乎所有编程语言的hello, world程序(3)
  • 原文地址:https://www.cnblogs.com/redirect/p/8658730.html
Copyright © 2011-2022 走看看