laravel5自带登录流程---源码解析
关于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 }
如果有什么问题。欢迎留下评论。