關於laravel的登錄流程有多少人和我一樣很好奇他的驗證和登錄。登錄流程是先驗證字段規則,驗證通過后,使用除密碼字段從數據庫篩選數據,將返回數據中的密碼和輸入的密碼作比較。我們先看登錄的form表單的action指向
http://localhost/auth/login。這里可以查看App/AuthController下的源代碼(有些地方可能解釋的不到位,對於laravel的Ioc還不太熟悉。):
1 use AuthenticatesAndRegistersUsers; 2 3 /** 4 * Create a new authentication controller instance. 5 * 6 * @param \Illuminate\Contracts\Auth\Guard $auth 7 * @param \Illuminate\Contracts\Auth\Registrar $registrar 8 * @return void 9 */ 10 public function __construct(Guard $auth, Registrar $registrar) 11 { 12 13 $this->auth = $auth; 14 $this->registrar = $registrar; 15 $this->middleware('guest', ['except' => 'getLogout']); 16 17 }
代碼很簡短。學過TP的人都會覺得很詫異,為啥沒有login方法,/auth/login就是交給AuthController的login方法處理。其實這里面登錄方法login都在成員變量auth中,這個auth是接口\Illuminate\Contracts\Auth\Guard類型,這里面就當java的指定類型的參數看待吧。但是這個\Illuminate\Contracts\Auth\Guard,進去看源代碼,發現這只是一個接口,laravel有一個原則,叫服務提供者和服務器容器。通俗的說,就是我給你一個服務容器,這里面可以隨便注入任何服務,你只需要構建提供服務的服務提供者。比如現在我有一個登陸服務容器,叫\Illuminate\Contracts\Auth\Guard,你可以構建a,b,服務提供者,但是這些提供者都要實現\Illuminate\Contracts\Auth\Guard接口。而服務容器和服務提供者的綁定,就要自己設定了。可以將服務容器和a綁定,也可以和b綁定,所以,當代碼中使用到這個服務容器的服務的時候就會交給綁定的服務提供者。關於更多的服務容器和服務提供者,請看官網手冊:http://www.golaravel.com/laravel/docs/5.0/providers/。AuthController中的服務容器\Illuminate\Contracts\Auth\Guard綁定到的服務提供者位於:Illuminate\Auth\Guard類。里面是不是有一個login和一個attempt的方法?;login方法是處理用戶登錄的,保存用戶的信息到session中等等,attempt方法是真正驗證用戶輸入的信息和數據庫的信息是否一致的操作。等會我們再說說是如何驗證數據庫信息的。
,regstrar是輔助驗證輸入的數據。之前一直想不通,laravel是如果將login方法調用的。再回頭仔細看AuthContrller中的用來關鍵字use來引入一個train。use AuthenticatesAndRegistersUsers;我們來看這個AuthenticatesAndRegistersUsers源代碼:
1 public function postLogin(Request $request)//見明之意,就是提交請求到login方法, 2 { 3 4 $this->validate($request, [ 5 'email' => 'required|email', 'password' => 'required',//調用validate驗證前端數據 6 ]); 7 8 $credentials = $request->only('email', 'password');//過濾掉前端數據,只留下email和password 9 10 11 if ($this->auth->attempt($credentials, $request->has('remember')))//重點就是這一個attempt方法,這個就是驗證用戶數據數據和數據庫數據作比較的流程 12 { 13 return redirect()->intended($this->redirectPath());//驗證通過則跳入主頁 14 } 15
16 return redirect($this->loginPath()) 17 //withInput(),負責數據寫入session 18 ->withInput($request->only('email', 'password'))//驗證失敗,即輸入數據和數據庫數據不一致,攜帶錯誤信息返回到登錄界面 19 //withErrors(), 20 ->withErrors([ 21 'email' => $this->getFailedLoginMessage(), 22 ]); 23 }
這個類是處理登錄和注冊操作的類。像里面會有postRegister,getLogin,getRegister都是處理登錄和注冊的代碼。更多的就不說了。我們現在關注attempt方法。上文提到服務提供者,這個$this->auth->attempt中auth是哪個服務提供者的是namespace Illuminate\Auth\Guard。看代碼中對於attempt方法是如何的:
1 /** 2 * Attempt to authenticate a user using the given credentials.//這句英文注釋,就解釋了,使用給定的輸入數據驗證用戶 3 * 4 * @param array $credentials 5 * @param bool $remember 6 * @param bool $login 7 * @return bool 8 */ 9 public function attempt(array $credentials = [], $remember = false, $login = true) 10 { 11 $this->fireAttemptEvent($credentials, $remember, $login);//這些是觸發監聽attempt事件的方法 12 13 $this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials);//這個是從數據庫中根據用戶的輸入從數據庫取出用戶的信息 14 15 // If an implementation of UserInterface was returned, we'll ask the provider 16 // to validate the user against the given credentials, and if they are in 17 // fact valid we'll log the users into the application and return true. 18 19 20 if ($this->hasValidCredentials($user, $credentials))//驗證密碼。用戶輸入的密碼和數據庫密碼是否一致 21 { 22 if ($login) $this->login($user, $remember);//進入login操作 23 24 return true; 25 } 26 27 return false; 28 }
關於login方法就不多說了。這個方法就是保存用戶的信息到sesion中,然后觸發監聽login事件的方法。關於$this->provider->retrieveByCredentials($credentials)取出用戶數據,這個就是上文提到的服務容器和服務提供者的概念。這里面的服務容器是Illuminate\Contracts\Auth\UserProvider,綁定的服務提供者是Illuminate\Auth\ EloquentUserProvider,這是根據config/auth配置下的driver選項設定的。Illuminate\Auth\ EloquentUserProvider的方法retrieveByCredentials使用除密碼字段外的字段選出用戶數據。發代碼了:
1 public function retrieveByCredentials(array $credentials) 2 { 3 // First we will add each credential element to the query as a where clause. 4 // Then we can execute the query and, if we found a user, return it in a 5 // Eloquent User "model" that will be utilized by the Guard instances. 6 $query = $this->createModel()->newQuery(); 7 8 foreach ($credentials as $key => $value) 9 { 10 if ( ! str_contains($key, 'password')) $query->where($key, $value);//拼接用戶輸入的字段構建where語句 11 } 12 13 return $query->first(); 14 }
關於密碼驗證的代碼。laravel注冊的時候對密碼的加密是使用hash算法。5.5以后提供的函數password_hash(),和password_veritify().根據服務提供者和服務容器的關系,可以追蹤代碼的驗證位於Illuminate\Hashing\BcryptHasher.代碼就是簡單的一句話:
1 public function check($value, $hashedValue, array $options = array()) 2 { 3 4 5 return password_verify($value, $hashedValue);//驗證成功,返回真,驗證失敗,返回false 6 }
如果有什么問題。歡迎留下評論。