Laravel中的Auth

laravel中有一个组件叫auth,auth组件提供了整个框架的认证功能,这里想简单追踪一下它的实现逻辑。

首先从 php artisan make:auth 开始

# Illuminate\Auth\Console\AuthMakeCommand.phppublic function handle(){    // 创建存放auth前端界面的目录和文件    // 模版存放在Auth\Console的stubs下    $this->createDirectories();    $this->exportViews();    if (! $this->option('views')) {        // 生成HomeController控制器文件        file_put_contents(            app_path('Http/Controllers/HomeController.php'),            $this->compileControllerStub()        );        // 生成auth相关路由    file_put_contents(        base_path('routes/web.php'),        file_get_contents(__DIR__.'/stubs/make/routes.stub'),        FILE_APPEND        );    }}

生成文件resources/views/auth、 resources/layouts路由文件 web.php 、和 Http/Controllers/Auth 下的控制器

说一说csrf-token

<!-- CSRF Token --><meta name="csrf-token" content="{{ csrf_token() }}">
        function csrf_token()    {        $session = app('session');        if (isset($session)) {            return $session->token();        }        throw new RuntimeException('Application session store not set.');    }

LoginController的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);    }

登录验证方法attemptLogin

通过 Auth::guard() 引导到 Illuminate\Auth\AuthManager
先看  服务提供者  AuthServiceProvider
AuthServiceProvider注册四个服务

protected function registerAuthenticator()    {        $this->app->singleton('auth', function ($app) {            $app['auth.loaded'] = true;                        // 生成一个AuthManager实例            return new AuthManager($app);        });        $this->app->singleton('auth.driver', function ($app) {            return $app['auth']->guard();        });    }
protected function registerUserResolver()    {        $this->app->bind(            AuthenticatableContract::class, function ($app) {                return call_user_func($app['auth']->userResolver());            }        );    }
protected function registerAccessGate()    {        $this->app->singleton(GateContract::class, function ($app) {            return new Gate($app, function () use ($app) {                return call_user_func($app['auth']->userResolver());            });        });    }
protected function registerRequestRebindHandler()    {        $this->app->rebinding('request', function ($app, $request) {            $request->setUserResolver(function ($guard = null) use ($app) {                return call_user_func($app['auth']->userResolver(), $guard);            });        });    }

生成一个AuthManager实例
AuthManager中的trait CreatesUserProviders
这个trait是用来绑定一个用户认证的Eloqument服务提供者

public function __construct($app)    {                // 绑定application实例        $this->app = $app;                // 绑定一个闭包,用于解析用户。                // 通过$guard来确定用户解析用户的方法        $this->userResolver = function ($guard = null) {            return $this->guard($guard)->user();        };    }protected function resolve($name)    {        $config = $this->getConfig($name);                // 根据配置调用不同的解析用户的驱动方法        $driverMethod = 'create'.ucfirst($config['driver']).'Driver';        if (method_exists($this, $driverMethod)) {            return $this->{$driverMethod}($name, $config);        }    }

分别定位的两个方法

        public function createSessionDriver($name, $config)    {                //     根据配置文件创建一个相应的provider        $provider = $this->createUserProvider($config['provider'] ?? null);                                $guard = new SessionGuard($name, $provider, $this->app['session.store']);        return $guard;    }        public function createTokenDriver($name, $config)    {        $guard = new TokenGuard(            $this->createUserProvider($config['provider'] ?? null),            $this->app['request'],            $config['input_key'] ?? 'api_token',            $config['storage_key'] ?? 'api_token'        );        return $guard;    }

于是得到 $this->guard($guard) 的user()方法
先看如何实例一个TokenGuard类

public function __construct(UserProvider $provider, Request $request, $inputKey = 'api_token', $storageKey = 'api_token')    {        $this->request = $request;        $this->provider = $provider;        $this->inputKey = $inputKey;        $this->storageKey = $storageKey;    }
# Illuminate\Auth\TokenGuard    public function user()    {        if (! is_null($this->user)) {            return $this->user;        }        $user = null;                // 从request中获取token        $token = $this->getTokenForRequest();        if (! empty($token)) {                        // 通过用户provider中的retrieveByCredentials方法来判断用户是否认证成功            $user = $this->provider->retrieveByCredentials(                [$this->storageKey => $token]            );        }        return $this->user = $user;    }

上面都是通用的加载引导调用功能,下面的用户服务提供者则是可以修改自定义的认证的具体功能

认证绑定的用户数据提供者

# Illuminate\Auth\DatabaseUserProvider    public function retrieveByCredentials(array $credentials)    {        if (empty($credentials) ||           (count($credentials) === 1 &&            array_key_exists('password', $credentials))) {            return;        }        $query = $this->conn->table($this->table);        foreach ($credentials as $key => $value) {            if (Str::contains($key, 'password')) {                continue;            }            if (is_array($value) || $value instanceof Arrayable) {                $query->whereIn($key, $value);            } else {                $query->where($key, $value);            }        }                $user = $query->first();                // 返回auth用户数据包        return $this->getGenericUser($user);    }