摘要:擴(kuò)展用戶認(rèn)證系統(tǒng)上一節(jié)我們介紹了系統(tǒng)實(shí)現(xiàn)的一些細(xì)節(jié)知道了是如何應(yīng)用看守器和用戶提供器來(lái)進(jìn)行用戶認(rèn)證的,但是針對(duì)我們自己開(kāi)發(fā)的項(xiàng)目或多或少地我們都會(huì)需要在自帶的看守器和用戶提供器基礎(chǔ)之上做一些定制化來(lái)適應(yīng)項(xiàng)目,本節(jié)我會(huì)列舉一個(gè)在做項(xiàng)目時(shí)遇到的
擴(kuò)展用戶認(rèn)證系統(tǒng)
上一節(jié)我們介紹了Laravel Auth系統(tǒng)實(shí)現(xiàn)的一些細(xì)節(jié)知道了Laravel是如何應(yīng)用看守器和用戶提供器來(lái)進(jìn)行用戶認(rèn)證的,但是針對(duì)我們自己開(kāi)發(fā)的項(xiàng)目或多或少地我們都會(huì)需要在自帶的看守器和用戶提供器基礎(chǔ)之上做一些定制化來(lái)適應(yīng)項(xiàng)目,本節(jié)我會(huì)列舉一個(gè)在做項(xiàng)目時(shí)遇到的具體案例,在這個(gè)案例中用自定義的看守器和用戶提供器來(lái)擴(kuò)展了Laravel的用戶認(rèn)證系統(tǒng)讓它能更適用于我們自己開(kāi)發(fā)的項(xiàng)目。
在介紹用戶認(rèn)證系統(tǒng)基礎(chǔ)的時(shí)候提到過(guò)Laravel自帶的注冊(cè)和登錄驗(yàn)證用戶密碼時(shí)都是去驗(yàn)證采用bcypt加密存儲(chǔ)的密碼,但是很多已經(jīng)存在的老系統(tǒng)中用戶密碼都是用鹽值加明文密碼做哈希后存儲(chǔ)的,如果想要在這種老系統(tǒng)中應(yīng)用Laravel開(kāi)發(fā)項(xiàng)目的話那么我們就不能夠再使用Laravel自帶的登錄和注冊(cè)方法了,下面我們就通過(guò)實(shí)例看看應(yīng)該如何擴(kuò)展Laravel的用戶認(rèn)證系統(tǒng)讓它能夠滿足我們項(xiàng)目的認(rèn)證需求。
修改用戶注冊(cè)首先我們將用戶注冊(cè)時(shí),用戶密碼的加密存儲(chǔ)的方式由bcypt加密后存儲(chǔ)改為由鹽值與明文密碼做哈希后再存儲(chǔ)的方式。這個(gè)非常簡(jiǎn)單,上一節(jié)已經(jīng)說(shuō)過(guò)Laravel自帶的用戶注冊(cè)方法是怎么實(shí)現(xiàn)了,這里我們直接將AppHttpControllersAuthRegisterController中的create方法修改為如下:
/** * Create a new user instance after a valid registration. * * @param array $data * @return User */ protected function create(array $data) { $salt = Str::random(6); return User::create([ "email" => $data["email"], "password" => sha1($salt . $data["password"]), "register_time" => time(), "register_ip" => ip2long(request()->ip()), "salt" => $salt ]); }
上面改完后注冊(cè)用戶后就能按照我們指定的方式來(lái)存儲(chǔ)用戶數(shù)據(jù)了,還有其他一些需要的與用戶信息相關(guān)的字段也需要存儲(chǔ)到用戶表中去這里就不再贅述了。
修改用戶登錄上節(jié)分析Laravel默認(rèn)登錄的實(shí)現(xiàn)細(xì)節(jié)時(shí)有說(shuō)登錄認(rèn)證的邏輯是通過(guò)SessionGuard的attempt方法來(lái)實(shí)現(xiàn)的,在attempt方法中SessionGuard通過(guò)EloquentUserProvider的retriveBycredentials方法從用戶表中查詢出用戶數(shù)據(jù),通過(guò) validateCredentials方法來(lái)驗(yàn)證給定的用戶認(rèn)證數(shù)據(jù)與從用戶表中查詢出來(lái)的用戶數(shù)據(jù)是否吻合。
下面直接給出之前講這塊時(shí)引用的代碼塊:
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)證通過(guò),通過(guò)login方法將用戶對(duì)象裝載到應(yīng)用里去 if ($this->hasValidCredentials($user, $credentials)) { $this->login($user, $remember); return true; } //登錄失敗的話,可以觸發(fā)事件通知用戶有可疑的登錄嘗試(需要自己定義listener來(lái)實(shí)現(xiàn)) $this->fireFailedEvent($user, $credentials); return false; } protected function hasValidCredentials($user, $credentials) { return ! is_null($user) && $this->provider->validateCredentials($user, $credentials); } } class EloquentUserProvider implements UserProvider { 從數(shù)據(jù)庫(kù)中取出用戶實(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(); } //通過(guò)給定用戶認(rèn)證數(shù)據(jù)來(lái)驗(yàn)證用戶 /** * Validate a user against the given credentials. * * @param IlluminateContractsAuthAuthenticatable $user * @param array $credentials * @return bool */ public function validateCredentials(UserContract $user, array $credentials) { $plain = $credentials["password"]; return $this->hasher->check($plain, $user->getAuthPassword()); } }自定義用戶提供器
好了, 看到這里就很明顯了, 我們需要改成自己的密碼驗(yàn)證就是自己實(shí)現(xiàn)一下validateCredentials就可以了, 修改$this->hasher->check為我們自己的密碼驗(yàn)證規(guī)則。
首先我們來(lái)重寫$user->getAuthPassword(); 在User模型中覆蓋其從父類中繼承來(lái)的這個(gè)方法,把數(shù)據(jù)庫(kù)中用戶表的salt和password傳遞到validateCredentials中來(lái):
class user extends Authenticatable { /** * 覆蓋Laravel中默認(rèn)的getAuthPassword方法, 返回用戶的password和salt字段 * @return array */ public function getAuthPassword() { return ["password" => $this->attributes["password"], "salt" => $this->attributes["salt"]]; } }
然后我們用一個(gè)自定義的用戶提供器,通過(guò)它的validateCredentials來(lái)實(shí)現(xiàn)我們自己系統(tǒng)的密碼驗(yàn)證規(guī)則,由于用戶提供器的其它方法不用改變沿用EloquentUserProvider里的實(shí)現(xiàn)就可以,所以我們讓自定義的用戶提供器繼承自EloquentUserProvider:
namespace AppFoundationAuth; use IlluminateAuthEloquentUserProvider; use IlluminateContractsAuthAuthenticatable; use IlluminateSupportStr; class CustomEloquentUserProvider extends EloquentUserProvider { /** * Validate a user against the given credentials. * * @param IlluminateContractsAuthAuthenticatable $user * @param array $credentials */ public function validateCredentials(Authenticatable $user, array $credentials) { $plain = $credentials["password"]; $authPassword = $user->getAuthPassword(); return sha1($authPassword["salt"] . $plain) == $authPassword["password"]; } }
接下來(lái)通過(guò)Auth::provider()將CustomEloquentUserProvider注冊(cè)到Laravel系統(tǒng)中,Auth::provider方法將一個(gè)返回用戶提供器對(duì)象的閉包作為用戶提供器創(chuàng)建器以給定名稱注冊(cè)到Laravel中,代碼如下:
class AppServiceProvider extends ServiceProvider { /** * Bootstrap any application services. * * @return void */ public function boot() { Auth::provider("custom-eloquent", function ($app, $config) { return New AppFoundationAuthCustomEloquentUserProvider($app["hash"], $config["model"]); }); } ...... }
注冊(cè)完用戶提供器后我們就可以在config/auth.php里配置讓看守器使用新注冊(cè)的custom-eloquent作為用戶提供器了:
//config/auth.php "providers" => [ "users" => [ "driver" => "coustom-eloquent", "model" => AppUser::class, ] ]自定義認(rèn)證看守器
好了,現(xiàn)在密碼認(rèn)證已經(jīng)修改過(guò)來(lái)了,現(xiàn)在用戶認(rèn)證使用的看守器還是SessionGuard, 在系統(tǒng)中會(huì)有對(duì)外提供API的模塊,在這種情形下我們一般希望用戶登錄認(rèn)證后會(huì)返回給客戶端一個(gè)JSON WEB TOKEN,每次調(diào)用接口時(shí)候通過(guò)這個(gè)token來(lái)認(rèn)證請(qǐng)求接口的是否是有效用戶,這個(gè)需求需要我們通過(guò)自定義的Guard擴(kuò)展功能來(lái)完成,有個(gè)composer包"tymon/jwt-auth": "dev-develop", 他的1.0beta版本帶的JwtGuard是一個(gè)實(shí)現(xiàn)了IlluminateContractsAuthGuard的看守器完全符合我上面說(shuō)的要求,所以我們就通過(guò)Auth::extend()方法將JwtGuard注冊(cè)到系統(tǒng)中去:
class AppServiceProvider extends ServiceProvider { /** * Bootstrap any application services. * * @return void */ public function boot() { Auth::provider("custom-eloquent", function ($app, $config) { return New AppFoundationAuthCustomEloquentUserProvider($app["hash"], $config["model"]); }); Auth::extend("jwt", function ($app, $name, array $config) { // 返回一個(gè) IlluminateContractsAuthGuard 實(shí)例... return new TymonJWTAuthJwtGuard(Auth::createUserProvider($config["provider"])); }); } ...... }
定義完之后,將?auth.php?配置文件的guards配置修改如下:
"guards" => [ "web" => [ "driver" => "session", "provider" => "users", ], "api" => [ "driver" => "jwt", // token ==> jwt "provider" => "users", ], ],
接下來(lái)我們定義一個(gè)API使用的登錄認(rèn)證方法, 在認(rèn)證中會(huì)使用上面注冊(cè)的jwt看守器來(lái)完成認(rèn)證,認(rèn)證完成后會(huì)返回一個(gè)JSON WEB TOKEN給客戶端
Route::post("apilogin", "AuthLoginController@apiLogin");
class LoginController extends Controller { public function apiLogin(Request $request) { ... if ($token = $this->guard("api")->attempt($credentials)) { $return["status_code"] = 200; $return["message"] = "登錄成功"; $response = Response::json($return); $response->headers->set("Authorization", "Bearer ". $token); return $response; } ... } }總結(jié)
通過(guò)上面的例子我們講解了如何通過(guò)自定義認(rèn)證看守器和用戶提供器擴(kuò)展Laravel的用戶認(rèn)證系統(tǒng),目的是讓大家對(duì)Laravel的用戶認(rèn)證系統(tǒng)有一個(gè)更好的理解知道在Laravel系統(tǒng)默認(rèn)自帶的用戶認(rèn)證方式無(wú)法滿足我們的需求時(shí)如何通過(guò)自定義這兩個(gè)組件來(lái)擴(kuò)展功能完成我們項(xiàng)目自己的認(rèn)證需求。
本文已經(jīng)收錄在系列文章Laravel源碼學(xué)習(xí)里,歡迎訪問(wèn)閱讀。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/28987.html
摘要:系統(tǒng)的核心是由的認(rèn)證組件的看守器和提供器組成。使用的認(rèn)證系統(tǒng),幾乎所有東西都已經(jīng)為你配置好了。其配置文件位于,其中包含了用于調(diào)整認(rèn)證服務(wù)行為的注釋清晰的選項(xiàng)配置。 用戶認(rèn)證系統(tǒng)(基礎(chǔ)介紹) 使用過(guò)Laravel的開(kāi)發(fā)者都知道,Laravel自帶了一個(gè)認(rèn)證系統(tǒng)來(lái)提供基本的用戶注冊(cè)、登錄、認(rèn)證、找回密碼,如果Auth系統(tǒng)里提供的基礎(chǔ)功能不滿足需求還可以很方便的在這些基礎(chǔ)功能上進(jìn)行擴(kuò)展。這篇...
摘要:通過(guò)裝載看守器和用戶提供器裝載看守器和用戶提供器用到的方法比較多,用文字描述不太清楚,我們通過(guò)注解這個(gè)過(guò)程中用到的方法來(lái)看具體的實(shí)現(xiàn)細(xì)節(jié)。 用戶認(rèn)證系統(tǒng)的實(shí)現(xiàn)細(xì)節(jié) 上一節(jié)我們介紹來(lái)Laravel Auth系統(tǒng)的基礎(chǔ)知識(shí),說(shuō)了他的核心組件都有哪些構(gòu)成,這一節(jié)我們會(huì)專注Laravel Auth系統(tǒng)的實(shí)現(xiàn)細(xì)節(jié),主要關(guān)注Auth也就是AuthManager是如何裝載認(rèn)證用的看守器(Guard)...
摘要:的契約是一組定義框架提供的核心服務(wù)的接口,例如我們?cè)诮榻B用戶認(rèn)證的章節(jié)中到的用戶看守器契約和用戶提供器契約以及框架自帶的模型所實(shí)現(xiàn)的契約。接口與團(tuán)隊(duì)開(kāi)發(fā)當(dāng)你的團(tuán)隊(duì)在開(kāi)發(fā)大型應(yīng)用時(shí),不同的部分有著不同的開(kāi)發(fā)速度。 Contracts Laravel 的契約是一組定義框架提供的核心服務(wù)的接口, 例如我們?cè)诮榻B用戶認(rèn)證的章節(jié)中到的用戶看守器契約IllumninateContractsAuth...
摘要:過(guò)去一年時(shí)間寫了多篇文章來(lái)探討了我認(rèn)為的框架最核心部分的設(shè)計(jì)思路代碼實(shí)現(xiàn)。為了大家閱讀方便,我把這些源碼學(xué)習(xí)的文章匯總到這里。數(shù)據(jù)庫(kù)算法和數(shù)據(jù)結(jié)構(gòu)這些都是編程的內(nèi)功,只有內(nèi)功深厚了才能解決遇到的復(fù)雜問(wèn)題。 過(guò)去一年時(shí)間寫了20多篇文章來(lái)探討了我認(rèn)為的Larave框架最核心部分的設(shè)計(jì)思路、代碼實(shí)現(xiàn)。通過(guò)更新文章自己在軟件設(shè)計(jì)、文字表達(dá)方面都有所提高,在剛開(kāi)始決定寫Laravel源碼分析地...
摘要:如何做用戶認(rèn)證根據(jù)文檔描述,提供用戶認(rèn)證的接口,他的核心是看守器和提供器,看守器定義怎么認(rèn)證用戶,提供器定義怎么檢索用戶。 最近的一個(gè)PHP項(xiàng)目,上一個(gè)項(xiàng)目是采用ThinkPHP來(lái)弄的,因?yàn)楹茉缇吐?tīng)說(shuō)過(guò)Laravel的大名,所以進(jìn)了Laravel的官網(wǎng),意外發(fā)現(xiàn)了Lumen,正好我項(xiàng)目是提供API的,所以選擇了Lumen,因?yàn)槭荓aravel的精簡(jiǎn)版,看了幾天的Laravel文檔,也總...
閱讀 1230·2021-11-23 10:04
閱讀 2473·2021-11-22 15:29
閱讀 3138·2021-11-19 09:40
閱讀 790·2021-09-22 15:26
閱讀 2190·2019-08-29 16:27
閱讀 2563·2019-08-29 16:10
閱讀 1977·2019-08-29 15:43
閱讀 3383·2019-08-29 12:43