日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > windows >内容正文

windows

Laravel核心代码学习--用户认证系统的实现细节

發(fā)布時(shí)間:2025/3/8 windows 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Laravel核心代码学习--用户认证系统的实现细节 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

用戶認(rèn)證系統(tǒng)的實(shí)現(xiàn)細(xì)節(jié)

上一節(jié)我們介紹了Laravel Auth系統(tǒng)的基礎(chǔ)知識(shí),說了他的核心組件都有哪些構(gòu)成,這一節(jié)我們會(huì)專注Laravel Auth系統(tǒng)的實(shí)現(xiàn)細(xì)節(jié),主要關(guān)注Auth也就是AuthManager是如何裝載認(rèn)證用的看守器(Guard)和用戶提供器(UserProvider)以及默認(rèn)的用戶注冊(cè)和登錄的實(shí)現(xiàn)細(xì)節(jié),通過梳理這些實(shí)現(xiàn)細(xì)節(jié)我們也就能知道應(yīng)該如何定制Auth認(rèn)證來滿足我們自己項(xiàng)目中用戶認(rèn)證的需求的。

通過AuthManager裝載看守器和用戶提供器

AuthManager裝載看守器和用戶提供器用到的方法比較多,用文字描述不太清楚,我們通過注解這個(gè)過程中用到的方法來看具體的實(shí)現(xiàn)細(xì)節(jié)。

namespace Illuminate\Auth; class AuthManager implements FactoryContract {/*** 嘗試從$guards屬性中獲取指定的Guard** @param string $name* @return \Illuminate\Contracts\Auth\Guard|\Illuminate\Contracts\Auth\StatefulGuard*/public function guard($name = null){$name = $name ?: $this->getDefaultDriver();return isset($this->guards[$name])? $this->guards[$name]: $this->guards[$name] = $this->resolve($name);}/*** 解析出給定name的Guard** @param string $name* @return \Illuminate\Contracts\Auth\Guard|\Illuminate\Contracts\Auth\StatefulGuard** @throws \InvalidArgumentException*/protected function resolve($name){//獲取Guard的配置//$config = ['driver' => 'session', 'provider' => 'users']$config = $this->getConfig($name);if (is_null($config)) {throw new InvalidArgumentException("Auth guard [{$name}] is not defined.");}//如果通過extend方法為guard定義了驅(qū)動(dòng)器,這里去調(diào)用自定義的Guard驅(qū)動(dòng)器if (isset($this->customCreators[$config['driver']])) {return $this->callCustomCreator($name, $config);}//Laravel auth默認(rèn)的配置這里是執(zhí)行createSessionDriver$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中獲取給定名稱的Guard的配置** @param string $name* @return array*/'guards' => ['web' => ['driver' => 'session','provider' => 'users',],'api' => ['driver' => 'token','provider' => 'users',],],protected function getConfig($name){//'guards' => [// 'web' => [// 'driver' => 'session',// 'provider' => 'users',// ],// 'api' => [// 'driver' => 'token',// 'provider' => 'users',// ],//],// 根據(jù)Laravel默認(rèn)的auth配置, 這個(gè)方法會(huì)獲取key "web"對(duì)應(yīng)的數(shù)組return $this->app['config']["auth.guards.{$name}"];}/*** 調(diào)用自定義的Guard驅(qū)動(dòng)器** @param string $name* @param array $config* @return mixed*/protected function callCustomCreator($name, array $config){return $this->customCreators[$config['driver']]($this->app, $name, $config);}/*** 注冊(cè)一個(gè)自定義的閉包Guard 驅(qū)動(dòng)器 到customCreators屬性中** @param string $driver* @param \Closure $callback* @return $this*/public function extend($driver, Closure $callback){$this->customCreators[$driver] = $callback;return $this;}/*** 注冊(cè)一個(gè)自定義的用戶提供器創(chuàng)建器到 customProviderCreators屬性中** @param string $name* @param \Closure $callback* @return $this*/public function provider($name, Closure $callback){$this->customProviderCreators[$name] = $callback;return $this;}/*** 創(chuàng)建基于session的認(rèn)證看守器 SessionGuard** @param string $name* @param array $config* @return \Illuminate\Auth\SessionGuard*/public function createSessionDriver($name, $config){//$config['provider'] == 'users'$provider = $this->createUserProvider($config['provider'] ?? null);$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;}//創(chuàng)建Guard驅(qū)動(dòng)依賴的用戶提供器對(duì)象public function createUserProvider($provider = null){if (is_null($config = $this->getProviderConfiguration($provider))) {return;}//如果通過Auth::provider方法注冊(cè)了自定義的用戶提供器creator閉包則去調(diào)用閉包獲取用戶提供器對(duì)象if (isset($this->customProviderCreators[$driver = ($config['driver'] ?? null)])) {return call_user_func($this->customProviderCreators[$driver], $this->app, $config);}switch ($driver) {case 'database':return $this->createDatabaseProvider($config);case 'eloquent'://通過默認(rèn)的auth配置這里會(huì)返回EloquentUserProvider對(duì)象,它實(shí)現(xiàn)了Illuminate\Contracts\Auth 接口return $this->createEloquentProvider($config);default:throw new InvalidArgumentException("Authentication user provider [{$driver}] is not defined.");}}/*** 會(huì)通過__call去動(dòng)態(tài)地調(diào)用AuthManager代理的Guard的用戶認(rèn)證相關(guān)方法* 根據(jù)默認(rèn)配置,這里__call會(huì)去調(diào)用SessionGuard里的方法* @param string $method* @param array $parameters* @return mixed*/public function __call($method, $parameters){return $this->guard()->{$method}(...$parameters);} }復(fù)制代碼

注冊(cè)用戶

Laravel Auth系統(tǒng)中默認(rèn)的注冊(cè)路由如下:

$this->post('register', 'Auth\RegisterController@register'); 復(fù)制代碼

所以用戶注冊(cè)的邏輯是由RegisterController的register方法來完成的

class RegisterController extends Controller {//方法定義在Illuminate\Foundation\Auth\RegisterUsers中public function register(Request $request){$this->validator($request->all())->validate();event(new Registered($user = $this->create($request->all())));$this->guard()->login($user);return $this->registered($request, $user)}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',]);}protected function create(array $data){return User::create(['name' => $data['name'],'email' => $data['email'],'password' => bcrypt($data['password']),]);}} 復(fù)制代碼

register的流程很簡單,就是驗(yàn)證用戶輸入的數(shù)據(jù)沒問題后將這些數(shù)據(jù)寫入數(shù)據(jù)庫生成用戶,其中密碼加密采用的是bcrypt算法,如果你需要改成常用的salt加密碼明文做哈希的密碼加密方法可以在create方法中對(duì)這部分邏輯進(jìn)行更改,注冊(cè)完用戶后會(huì)調(diào)用SessionGuard的login方法把用戶數(shù)據(jù)裝載到應(yīng)用中,注意這個(gè)login方法沒有登錄認(rèn)證,只是把認(rèn)證后的用戶裝載到應(yīng)用中這樣在應(yīng)用里任何地方我們都能夠通過Auth::user()來獲取用戶數(shù)據(jù)啦。

用戶登錄認(rèn)證

Laravel Auth系統(tǒng)的登錄路由如下

$this->post('login', 'Auth\LoginController@login'); 復(fù)制代碼

我們看一下LoginController里的登錄邏輯

class LoginController extends Controller {/*** 處理登錄請(qǐng)求*/public function login(Request $request){//驗(yàn)證登錄字段$this->validateLogin($request);//防止惡意的多次登錄嘗試if ($this->hasTooManyLoginAttempts($request)) {$this->fireLockoutEvent($request);return $this->sendLockoutResponse($request);}//進(jìn)行登錄認(rèn)證if ($this->attemptLogin($request)) {return $this->sendLoginResponse($request);}$this->incrementLoginAttempts($request);return $this->sendFailedLoginResponse($request);}//嘗試進(jìn)行登錄認(rèn)證protected function attemptLogin(Request $request){return $this->guard()->attempt($this->credentials($request), $request->filled('remember'));}//獲取登錄用的字段值protected function credentials(Request $request){return $request->only($this->username(), 'password');} } 復(fù)制代碼

可以看到,登錄認(rèn)證的邏輯是通過SessionGuard的attempt方法來實(shí)現(xiàn)的,其實(shí)就是Auth::attempt(), 下面我們來看看attempt方法里的邏輯:

class SessionGuard implements StatefulGuard, SupportsBasicAuth {public function attempt(array $credentials = [], $remember = false){$this->fireAttemptEvent($credentials, $remember);$this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials);//如果登錄認(rèn)證通過,通過login方法將用戶對(duì)象裝載到應(yīng)用里去if ($this->hasValidCredentials($user, $credentials)) {$this->login($user, $remember);return true;}//登錄失敗的話,可以觸發(fā)事件通知用戶有可疑的登錄嘗試(需要自己定義listener來實(shí)現(xiàn))$this->fireFailedEvent($user, $credentials);return false;}protected function hasValidCredentials($user, $credentials){return ! is_null($user) && $this->provider->validateCredentials($user, $credentials);} } 復(fù)制代碼

SessionGuard的attempt方法首先通過用戶提供器的retriveBycredentials方法通過用戶名從用戶表中查詢出用戶數(shù)據(jù),認(rèn)證用戶信息是通過用戶提供器的validateCredentials來實(shí)現(xiàn)的,所有用戶提供器的實(shí)現(xiàn)類都會(huì)實(shí)現(xiàn)UserProvider契約(interface)中定義的方法,通過上面的分析我們知道默認(rèn)的用戶提供器是EloquentUserProvider

class EloquentUserProvider implements UserProvider {從數(shù)據(jù)庫中取出用戶實(shí)例public function retrieveByCredentials(array $credentials){if (empty($credentials) ||(count($credentials) === 1 &&array_key_exists('password', $credentials))) {return;}$query = $this->createModel()->newQuery();foreach ($credentials as $key => $value) {if (! Str::contains($key, 'password')) {$query->where($key, $value);}}return $query->first();}//通過給定用戶認(rèn)證數(shù)據(jù)來驗(yàn)證用戶public function validateCredentials(UserContract $user, array $credentials){$plain = $credentials['password'];return $this->hasher->check($plain, $user->getAuthPassword());} }class BcryptHasher implements HasherContract {//通過bcrypt算法計(jì)算給定value的散列值public function make($value, array $options = []){$hash = password_hash($value, PASSWORD_BCRYPT, ['cost' => $this->cost($options),]);if ($hash === false) {throw new RuntimeException('Bcrypt hashing not supported.');}return $hash;}//驗(yàn)證散列值是否給定明文值通過bcrypt算法計(jì)算得到的public function check($value, $hashedValue, array $options = []){if (strlen($hashedValue) === 0) {return false;}return password_verify($value, $hashedValue);} } 復(fù)制代碼

用戶密碼的驗(yàn)證是通過EloquentUserProvider依賴的hasher哈希器來完成的,Laravel認(rèn)證系統(tǒng)默認(rèn)采用bcrypt算法來加密用戶提供的明文密碼然后存儲(chǔ)到用戶表里的,驗(yàn)證時(shí)haser哈希器的check方法會(huì)通過PHP內(nèi)建方法password_verify來驗(yàn)證明文密碼是否是存儲(chǔ)的密文密碼的原值。

用戶認(rèn)證系統(tǒng)的主要細(xì)節(jié)梳理完后我們就知道如何定義我們自己的看守器(Guard)或用戶提供器(UserProvider)了,首先他們必須實(shí)現(xiàn)各自遵守的契約里的方法才能夠無縫接入到Laravel的Auth系統(tǒng)中,然后還需要將自己定義的Guard或Provider通過Auth::extend、Auth::provider方法注冊(cè)返回Guard或者Provider實(shí)例的閉包到Laravel中去,Guard和UserProvider的自定義不是必須成套的,我們可以單獨(dú)自定義Guard仍使用默認(rèn)的EloquentUserProvider,或者讓默認(rèn)的SessionGuard使用自定義的UserProvider。

下一節(jié)我會(huì)給出一個(gè)我們以前項(xiàng)目開發(fā)中用到的一個(gè)案例來更好地講解應(yīng)該如何對(duì)Laravel Auth系統(tǒng)進(jìn)行擴(kuò)展。

本文已經(jīng)收錄在系列文章Laravel源碼學(xué)習(xí)里,歡迎訪問閱讀。

總結(jié)

以上是生活随笔為你收集整理的Laravel核心代码学习--用户认证系统的实现细节的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。