摘要:擴展用戶認證系統上一節我們介紹了系統實現的一些細節知道了是如何應用看守器和用戶提供器來進行用戶認證的,但是針對我們自己開發的項目或多或少地我們都會需要在自帶的看守器和用戶提供器基礎之上做一些定制化來適應項目,本節我會列舉一個在做項目時遇到的
擴展用戶認證系統
上一節我們介紹了Laravel Auth系統實現的一些細節知道了Laravel是如何應用看守器和用戶提供器來進行用戶認證的,但是針對我們自己開發的項目或多或少地我們都會需要在自帶的看守器和用戶提供器基礎之上做一些定制化來適應項目,本節我會列舉一個在做項目時遇到的具體案例,在這個案例中用自定義的看守器和用戶提供器來擴展了Laravel的用戶認證系統讓它能更適用于我們自己開發的項目。
在介紹用戶認證系統基礎的時候提到過Laravel自帶的注冊和登錄驗證用戶密碼時都是去驗證采用bcypt加密存儲的密碼,但是很多已經存在的老系統中用戶密碼都是用鹽值加明文密碼做哈希后存儲的,如果想要在這種老系統中應用Laravel開發項目的話那么我們就不能夠再使用Laravel自帶的登錄和注冊方法了,下面我們就通過實例看看應該如何擴展Laravel的用戶認證系統讓它能夠滿足我們項目的認證需求。
修改用戶注冊首先我們將用戶注冊時,用戶密碼的加密存儲的方式由bcypt加密后存儲改為由鹽值與明文密碼做哈希后再存儲的方式。這個非常簡單,上一節已經說過Laravel自帶的用戶注冊方法是怎么實現了,這里我們直接將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 ]); }
上面改完后注冊用戶后就能按照我們指定的方式來存儲用戶數據了,還有其他一些需要的與用戶信息相關的字段也需要存儲到用戶表中去這里就不再贅述了。
修改用戶登錄上節分析Laravel默認登錄的實現細節時有說登錄認證的邏輯是通過SessionGuard的attempt方法來實現的,在attempt方法中SessionGuard通過EloquentUserProvider的retriveBycredentials方法從用戶表中查詢出用戶數據,通過 validateCredentials方法來驗證給定的用戶認證數據與從用戶表中查詢出來的用戶數據是否吻合。
下面直接給出之前講這塊時引用的代碼塊:
class SessionGuard implements StatefulGuard, SupportsBasicAuth { public function attempt(array $credentials = [], $remember = false) { $this->fireAttemptEvent($credentials, $remember); $this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials); //如果登錄認證通過,通過login方法將用戶對象裝載到應用里去 if ($this->hasValidCredentials($user, $credentials)) { $this->login($user, $remember); return true; } //登錄失敗的話,可以觸發事件通知用戶有可疑的登錄嘗試(需要自己定義listener來實現) $this->fireFailedEvent($user, $credentials); return false; } protected function hasValidCredentials($user, $credentials) { return ! is_null($user) && $this->provider->validateCredentials($user, $credentials); } } class EloquentUserProvider implements UserProvider { 從數據庫中取出用戶實例 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(); } //通過給定用戶認證數據來驗證用戶 /** * 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()); } }自定義用戶提供器
好了, 看到這里就很明顯了, 我們需要改成自己的密碼驗證就是自己實現一下validateCredentials就可以了, 修改$this->hasher->check為我們自己的密碼驗證規則。
首先我們來重寫$user->getAuthPassword(); 在User模型中覆蓋其從父類中繼承來的這個方法,把數據庫中用戶表的salt和password傳遞到validateCredentials中來:
class user extends Authenticatable { /** * 覆蓋Laravel中默認的getAuthPassword方法, 返回用戶的password和salt字段 * @return array */ public function getAuthPassword() { return ["password" => $this->attributes["password"], "salt" => $this->attributes["salt"]]; } }
然后我們用一個自定義的用戶提供器,通過它的validateCredentials來實現我們自己系統的密碼驗證規則,由于用戶提供器的其它方法不用改變沿用EloquentUserProvider里的實現就可以,所以我們讓自定義的用戶提供器繼承自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系統中,Auth::provider方法將一個返回用戶提供器對象的閉包作為用戶提供器創建器以給定名稱注冊到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, ] ]自定義認證看守器
好了,現在密碼認證已經修改過來了,現在用戶認證使用的看守器還是SessionGuard, 在系統中會有對外提供API的模塊,在這種情形下我們一般希望用戶登錄認證后會返回給客戶端一個JSON WEB TOKEN,每次調用接口時候通過這個token來認證請求接口的是否是有效用戶,這個需求需要我們通過自定義的Guard擴展功能來完成,有個composer包"tymon/jwt-auth": "dev-develop", 他的1.0beta版本帶的JwtGuard是一個實現了IlluminateContractsAuthGuard的看守器完全符合我上面說的要求,所以我們就通過Auth::extend()方法將JwtGuard注冊到系統中去:
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) { // 返回一個 IlluminateContractsAuthGuard 實例... return new TymonJWTAuthJwtGuard(Auth::createUserProvider($config["provider"])); }); } ...... }
定義完之后,將?auth.php?配置文件的guards配置修改如下:
"guards" => [ "web" => [ "driver" => "session", "provider" => "users", ], "api" => [ "driver" => "jwt", // token ==> jwt "provider" => "users", ], ],
接下來我們定義一個API使用的登錄認證方法, 在認證中會使用上面注冊的jwt看守器來完成認證,認證完成后會返回一個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; } ... } }總結
通過上面的例子我們講解了如何通過自定義認證看守器和用戶提供器擴展Laravel的用戶認證系統,目的是讓大家對Laravel的用戶認證系統有一個更好的理解知道在Laravel系統默認自帶的用戶認證方式無法滿足我們的需求時如何通過自定義這兩個組件來擴展功能完成我們項目自己的認證需求。
本文已經收錄在系列文章Laravel源碼學習里,歡迎訪問閱讀。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/28987.html
摘要:系統的核心是由的認證組件的看守器和提供器組成。使用的認證系統,幾乎所有東西都已經為你配置好了。其配置文件位于,其中包含了用于調整認證服務行為的注釋清晰的選項配置。 用戶認證系統(基礎介紹) 使用過Laravel的開發者都知道,Laravel自帶了一個認證系統來提供基本的用戶注冊、登錄、認證、找回密碼,如果Auth系統里提供的基礎功能不滿足需求還可以很方便的在這些基礎功能上進行擴展。這篇...
摘要:通過裝載看守器和用戶提供器裝載看守器和用戶提供器用到的方法比較多,用文字描述不太清楚,我們通過注解這個過程中用到的方法來看具體的實現細節。 用戶認證系統的實現細節 上一節我們介紹來Laravel Auth系統的基礎知識,說了他的核心組件都有哪些構成,這一節我們會專注Laravel Auth系統的實現細節,主要關注Auth也就是AuthManager是如何裝載認證用的看守器(Guard)...
摘要:的契約是一組定義框架提供的核心服務的接口,例如我們在介紹用戶認證的章節中到的用戶看守器契約和用戶提供器契約以及框架自帶的模型所實現的契約。接口與團隊開發當你的團隊在開發大型應用時,不同的部分有著不同的開發速度。 Contracts Laravel 的契約是一組定義框架提供的核心服務的接口, 例如我們在介紹用戶認證的章節中到的用戶看守器契約IllumninateContractsAuth...
摘要:過去一年時間寫了多篇文章來探討了我認為的框架最核心部分的設計思路代碼實現。為了大家閱讀方便,我把這些源碼學習的文章匯總到這里。數據庫算法和數據結構這些都是編程的內功,只有內功深厚了才能解決遇到的復雜問題。 過去一年時間寫了20多篇文章來探討了我認為的Larave框架最核心部分的設計思路、代碼實現。通過更新文章自己在軟件設計、文字表達方面都有所提高,在剛開始決定寫Laravel源碼分析地...
摘要:如何做用戶認證根據文檔描述,提供用戶認證的接口,他的核心是看守器和提供器,看守器定義怎么認證用戶,提供器定義怎么檢索用戶。 最近的一個PHP項目,上一個項目是采用ThinkPHP來弄的,因為很早就聽說過Laravel的大名,所以進了Laravel的官網,意外發現了Lumen,正好我項目是提供API的,所以選擇了Lumen,因為是Laravel的精簡版,看了幾天的Laravel文檔,也總...
閱讀 1141·2021-11-23 10:04
閱讀 2401·2021-11-22 15:29
閱讀 2743·2021-11-19 09:40
閱讀 715·2021-09-22 15:26
閱讀 2117·2019-08-29 16:27
閱讀 2484·2019-08-29 16:10
閱讀 1918·2019-08-29 15:43
閱讀 3275·2019-08-29 12:43