Laravel 中的 Auth
laravel 中有一个组件叫 auth,auth 组件提供了整个框架的认证功能,这里想简单追踪一下它的实现逻辑。
首先从 php artisan make:auth
开始
# Illuminate\Auth\Console\AuthMakeCommand.php
public 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);
}