php 登录原理,Laravel 登录原理剖析

简介

Laravel 中实现用户认证非常简单。实际上,几乎所有东西都已经为你配置好了。其配置文件位于 config/auth.php,其中包含了用于调整认证服务行为的注释清晰的选项配置。

其核心是由 Laravel 的认证组件的「看守器」和「提供器」组成。看守器定义了该如何认证每个请求中用户。例如,Laravel 自带的 session 看守器会使用 session 存储和 cookies 来维护状态。

提供器中定义了该如何从持久化的存储数据中检索用户。Laravel 自带支持使用 Eloquent 和数据库查询构造器来检索用户。当然,你可以根据需要自定义其他提供器。

不过对大多数应用而言,可能永远都不需要修改默认身份认证配置。

上面的简介出自 Laravel-China 社区的文档中。

除此之外,Laravel还提供了一个简单的命令来快速生成身份验证所需的路由和视图:

php artisan make:auth

原理剖析

Auth::routes()

make:auth命令在routes/web.php中插入了下面的代码:

Auth::routes();

其中,Auth 是使用 Facades 来调用的。那么我们可以在文件vendor/laravel/framework/src/Illuminate/Support/Facades/Auth.php中找到routes方法。

// Laravel5.8 中的 routes 方法

public static function routes(array $options = [])

{

static::$app->make('router')->auth($options);

}

// Laravel5.5 中的 routes 方法

public static function routes()

{

static::$app->make('router')->auth();

}

在这个方法中,调用了vendor/laravel/framework/src/Illuminate/Routing/Router.php文件中的 auth 方法。

// Laravel5.8 中的 auth 方法

public function auth(array $options = [])

{

// 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...

if ($options['register'] ?? true) {

$this->get('register', 'Auth\RegisterController@showRegistrationForm')->name('register');

$this->post('register', 'Auth\RegisterController@register');

}

// Password Reset Routes...

if ($options['reset'] ?? true) {

$this->resetPassword();

}

// Email Verification Routes...

if ($options['verify'] ?? false) {

$this->emailVerification();

}

}

// Laravel 5.5 中的 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');

}

从上面的代码中我们可以看到,auth 方法为我们注册了登录、登出、密码重设等路由。而在 Laravel5.8 中我们则可以在Auth::routes()中传入参数来控制个别路由是否注册,比如:

Auth::routes([

'register' => false,

'reset' => false,

'verify' => true

]);

可以禁用注册和重置路由,启用邮箱验证路由。

登录原理

在LoginController中引入了Illuminate\Foundation\Auth\AuthenticatesUsers这个 trait。登录的逻辑使用了其中的 login 方法。trait 文件中的相关代码如下:

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);

}

具体逻辑为首先验证表单提交字段是否通过验证,即$this->validateLogin($request)方法,下面的第一个 if 代码块用来判断是否超过登录次数限制。接着判断用户能否进行登录。即$this->attemptLogin($request)方法。

protected function attemptLogin(Request $request)

{

return $this->guard()->attempt(

$this->credentials($request), $request->filled('remember')

);

}

protected function guard()

{

return Auth::guard();

}

protected function credentials(Request $request)

{

return $request->only($this->username(), 'password');

}

通过上面的代码可以看到,我们调用 Auth::guard() 来判断用户能否登录,如果认证通过那么用户登录成功,否则登录失败。至于这个 Guard 的工作原理,我们下面详细说明。

Auth::guard()->attempt()

AuthManager

由于 Auth 的 Facades 对应的底层类为Illuminate\Auth\AuthManager,因此我们首先分析这个类。

public function guard($name = null)

{

$name = $name ?: $this->getDefaultDriver();

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

}

public function getDefaultDriver()

{

return $this->app['config']['auth.defaults.guard'];

}

在登录的逻辑中,由于没有传入特定的参数,因为我们将会调用默认的 Driver 和 Provider。即config/auth.php中的defaults 配置:

'defaults' => [

'guard' => 'web',

'passwords' => 'users',

],

...

'guards' => [

'web' => [

'driver' => 'session',

'provider' => 'users',

],

...

],

'providers' => [

'users' => [

'driver' => 'eloquent',

'model' => App\User::class,

],

],

'passwords' => [

'users' => [

'provider' => 'users',

'table' => 'password_resets',

'expire' => 60,

],

],

通过配置文件可以看到,我们使用的 driver 是 session driver 和 eloquent provider。在 createSessionDriver 方法中我们新建了一个SessionGuard。

因此 Auth::guard() 返回的是一个 SessionGuard 类。

在 SessionGuard 中我们可以看到有 attempt 方法。

public function attempt(array $credentials = [], $remember = false)

{

$this->fireAttemptEvent($credentials, $remember);

$this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials);

if ($this->hasValidCredentials($user, $credentials)) {

$this->login($user, $remember);

return true;

}

$this->fireFailedEvent($user, $credentials);

return false;

}

protected function hasValidCredentials($user, $credentials)

{

return ! is_null($user) && $this->provider->validateCredentials($user, $credentials);

}

public function login(AuthenticatableContract $user, $remember = false)

{

$this->updateSession($user->getAuthIdentifier());

if ($remember) {

$this->ensureRememberTokenIsSet($user);

$this->queueRecallerCookie($user);

}

$this->fireLoginEvent($user, $remember);

$this->setUser($user);

}

在这里我们可以看到首先触发 attempt 事件。接着我们通过 EloquentUserProvider 中的retrieveByCredentials 得到除 password 字段外匹配的 User 模型。如果匹配成功,则判断密码是否一致,如果一致,则登录成功。否则失败。

注:密码校验默认使用 bcrypt。

EloquentUserProvider 中相关代码如下:

public function retrieveByCredentials(array $credentials)

{

if (empty($credentials) ||

(count($credentials) === 1 &&

array_key_exists('password', $credentials))) {

return;

}

$query = $this->newModelQuery();

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);

}

}

return $query->first();

}

public function validateCredentials(UserContract $user, array $credentials)

{

$plain = $credentials['password'];

return $this->hasher->check($plain, $user->getAuthPassword());

}

这基本上就是登录过程的原理。

本作品采用《CC 协议》,转载必须注明作者和本文链接

There's nothing wrong with having a little fun.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值