摘要:擴(kuò)展用戶認(rèn)證系統(tǒng)上一節(jié)我們介紹了系統(tǒng)實(shí)現(xiàn)的一些細(xì)節(jié)知道了是如何應(yīng)用看守器和用戶提供器來進(jìn)行用戶認(rèn)證的,但是針對我們自己開發(fā)的項(xiàng)目或多或少地我們都會需要在自帶的看守器和用戶提供器基礎(chǔ)之上做一些定制化來適應(yīng)項(xiàng)目,本節(jié)我會列舉一個(gè)在做項(xiàng)目時(shí)遇到的
擴(kuò)展用戶認(rèn)證系統(tǒng)
上一節(jié)我們介紹了Laravel Auth系統(tǒng)實(shí)現(xiàn)的一些細(xì)節(jié)知道了Laravel是如何應(yīng)用看守器和用戶提供器來進(jìn)行用戶認(rèn)證的,但是針對我們自己開發(fā)的項(xiàng)目或多或少地我們都會需要在自帶的看守器和用戶提供器基礎(chǔ)之上做一些定制化來適應(yīng)項(xiàng)目,本節(jié)我會列舉一個(gè)在做項(xiàng)目時(shí)遇到的具體案例,在這個(gè)案例中用自定義的看守器和用戶提供器來擴(kuò)展了Laravel的用戶認(rèn)證系統(tǒng)讓它能更適用于我們自己開發(fā)的項(xiàng)目。
在介紹用戶認(rèn)證系統(tǒng)基礎(chǔ)的時(shí)候提到過Laravel自帶的注冊和登錄驗(yàn)證用戶密碼時(shí)都是去驗(yàn)證采用bcypt加密存儲的密碼,但是很多已經(jīng)存在的老系統(tǒng)中用戶密碼都是用鹽值加明文密碼做哈希后存儲的,如果想要在這種老系統(tǒng)中應(yīng)用Laravel開發(fā)項(xiàng)目的話那么我們就不能夠再使用Laravel自帶的登錄和注冊方法了,下面我們就通過實(shí)例看看應(yīng)該如何擴(kuò)展Laravel的用戶認(rèn)證系統(tǒng)讓它能夠滿足我們項(xiàng)目的認(rèn)證需求。
修改用戶注冊首先我們將用戶注冊時(shí),用戶密碼的加密存儲的方式由bcypt加密后存儲改為由鹽值與明文密碼做哈希后再存儲的方式。這個(gè)非常簡單,上一節(jié)已經(jīng)說過Laravel自帶的用戶注冊方法是怎么實(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 ]); }
上面改完后注冊用戶后就能按照我們指定的方式來存儲用戶數(shù)據(jù)了,還有其他一些需要的與用戶信息相關(guān)的字段也需要存儲到用戶表中去這里就不再贅述了。
修改用戶登錄上節(jié)分析Laravel默認(rèn)登錄的實(shí)現(xiàn)細(xì)節(jié)時(shí)有說登錄認(rèn)證的邏輯是通過SessionGuard的attempt方法來實(shí)現(xiàn)的,在attempt方法中SessionGuard通過EloquentUserProvider的retriveBycredentials方法從用戶表中查詢出用戶數(shù)據(jù),通過 validateCredentials方法來驗(yàn)證給定的用戶認(rèn)證數(shù)據(jù)與從用戶表中查詢出來的用戶數(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)證通過,通過login方法將用戶對象裝載到應(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); } } 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)證用戶 /** * 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ī)則。
首先我們來重寫$user->getAuthPassword(); 在User模型中覆蓋其從父類中繼承來的這個(gè)方法,把數(shù)據(jù)庫中用戶表的salt和password傳遞到validateCredentials中來:
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è)自定義的用戶提供器,通過它的validateCredentials來實(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"]; } }
接下來通過Auth::provider()將CustomEloquentUserProvider注冊到Laravel系統(tǒng)中,Auth::provider方法將一個(gè)返回用戶提供器對象的閉包作為用戶提供器創(chuàng)建器以給定名稱注冊到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"]); }); } ...... }
注冊完用戶提供器后我們就可以在config/auth.php里配置讓看守器使用新注冊的custom-eloquent作為用戶提供器了:
//config/auth.php "providers" => [ "users" => [ "driver" => "coustom-eloquent", "model" => AppUser::class, ] ]自定義認(rèn)證看守器
好了,現(xiàn)在密碼認(rèn)證已經(jīng)修改過來了,現(xiàn)在用戶認(rèn)證使用的看守器還是SessionGuard, 在系統(tǒng)中會有對外提供API的模塊,在這種情形下我們一般希望用戶登錄認(rèn)證后會返回給客戶端一個(gè)JSON WEB TOKEN,每次調(diào)用接口時(shí)候通過這個(gè)token來認(rèn)證請求接口的是否是有效用戶,這個(gè)需求需要我們通過自定義的Guard擴(kuò)展功能來完成,有個(gè)composer包"tymon/jwt-auth": "dev-develop", 他的1.0beta版本帶的JwtGuard是一個(gè)實(shí)現(xiàn)了IlluminateContractsAuthGuard的看守器完全符合我上面說的要求,所以我們就通過Auth::extend()方法將JwtGuard注冊到系統(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", ], ],
接下來我們定義一個(gè)API使用的登錄認(rèn)證方法, 在認(rèn)證中會使用上面注冊的jwt看守器來完成認(rèn)證,認(rèn)證完成后會返回一個(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é)
通過上面的例子我們講解了如何通過自定義認(rèn)證看守器和用戶提供器擴(kuò)展Laravel的用戶認(rèn)證系統(tǒng),目的是讓大家對Laravel的用戶認(rèn)證系統(tǒng)有一個(gè)更好的理解知道在Laravel系統(tǒng)默認(rèn)自帶的用戶認(rèn)證方式無法滿足我們的需求時(shí)如何通過自定義這兩個(gè)組件來擴(kuò)展功能完成我們項(xiàng)目自己的認(rèn)證需求。
本文已經(jīng)收錄在系列文章Laravel源碼學(xué)習(xí)里,歡迎訪問閱讀。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.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ǔ)介紹) 使用過Laravel的開發(fā)者都知道,Laravel自帶了一個(gè)認(rèn)證系統(tǒng)來提供基本的用戶注冊、登錄、認(rèn)證、找回密碼,如果Auth系統(tǒng)里提供的基礎(chǔ)功能不滿足需求還可以很方便的在這些基礎(chǔ)功能上進(jìn)行擴(kuò)展。這篇...
摘要:通過裝載看守器和用戶提供器裝載看守器和用戶提供器用到的方法比較多,用文字描述不太清楚,我們通過注解這個(gè)過程中用到的方法來看具體的實(shí)現(xiàn)細(xì)節(jié)。 用戶認(rèn)證系統(tǒng)的實(shí)現(xiàn)細(xì)節(jié) 上一節(jié)我們介紹來Laravel Auth系統(tǒng)的基礎(chǔ)知識,說了他的核心組件都有哪些構(gòu)成,這一節(jié)我們會專注Laravel Auth系統(tǒng)的實(shí)現(xiàn)細(xì)節(jié),主要關(guān)注Auth也就是AuthManager是如何裝載認(rèn)證用的看守器(Guard)...
摘要:的契約是一組定義框架提供的核心服務(wù)的接口,例如我們在介紹用戶認(rèn)證的章節(jié)中到的用戶看守器契約和用戶提供器契約以及框架自帶的模型所實(shí)現(xiàn)的契約。接口與團(tuán)隊(duì)開發(fā)當(dāng)你的團(tuán)隊(duì)在開發(fā)大型應(yīng)用時(shí),不同的部分有著不同的開發(fā)速度。 Contracts Laravel 的契約是一組定義框架提供的核心服務(wù)的接口, 例如我們在介紹用戶認(rèn)證的章節(jié)中到的用戶看守器契約IllumninateContractsAuth...
摘要:過去一年時(shí)間寫了多篇文章來探討了我認(rèn)為的框架最核心部分的設(shè)計(jì)思路代碼實(shí)現(xiàn)。為了大家閱讀方便,我把這些源碼學(xué)習(xí)的文章匯總到這里。數(shù)據(jù)庫算法和數(shù)據(jù)結(jié)構(gòu)這些都是編程的內(nèi)功,只有內(nèi)功深厚了才能解決遇到的復(fù)雜問題。 過去一年時(shí)間寫了20多篇文章來探討了我認(rèn)為的Larave框架最核心部分的設(shè)計(jì)思路、代碼實(shí)現(xiàn)。通過更新文章自己在軟件設(shè)計(jì)、文字表達(dá)方面都有所提高,在剛開始決定寫Laravel源碼分析地...
摘要:如何做用戶認(rèn)證根據(jù)文檔描述,提供用戶認(rèn)證的接口,他的核心是看守器和提供器,看守器定義怎么認(rèn)證用戶,提供器定義怎么檢索用戶。 最近的一個(gè)PHP項(xiàng)目,上一個(gè)項(xiàng)目是采用ThinkPHP來弄的,因?yàn)楹茉缇吐犝f過Laravel的大名,所以進(jìn)了Laravel的官網(wǎng),意外發(fā)現(xiàn)了Lumen,正好我項(xiàng)目是提供API的,所以選擇了Lumen,因?yàn)槭荓aravel的精簡版,看了幾天的Laravel文檔,也總...
閱讀 1152·2021-11-23 10:04
閱讀 2412·2021-11-22 15:29
閱讀 2806·2021-11-19 09:40
閱讀 729·2021-09-22 15:26
閱讀 2129·2019-08-29 16:27
閱讀 2498·2019-08-29 16:10
閱讀 1932·2019-08-29 15:43
閱讀 3285·2019-08-29 12:43