Laravel 5.3 登录注册底层实现详解
每个控制器都使用 trait 来引入它们需要的方法 */
用于处理用户登录认证
用于处理新用户注册
包含重置密码逻辑
用于处理重置密码邮件链接
认证需要的视图
包含了应用的基础布局文件
Auth::routes();
static::$app->make('router')->auth();
注册
public function showRegistrationForm()
{
return view('auth.register');
}
return view('auth.register');
public function register(Request $request)
{
$this->validator($request->all())->validate();
event(new Registered($user = $this->create($request->all())));
$this->guard()->login($user);
return $this->registered($request, $user)
?: redirect($this->redirectPath());
}
1. $this->validator($request->all())->validate();
$this->validator() protected function validator(array $data)
{
return Validator::make($data, [
'name' => 'required|max:255',
'email' => 'required|email|max:255|unique:users',
'password' => 'required|min:6|confirmed',
]);
}
如果我们的用户注册需要的表单与这几个字段不一致(例如需要添加一个手机号),就在这里修改
event(new Registered($user = $this->create($request->all())));
protected function create(array $data)
{
return User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => bcrypt($data['password']),
]);
}
利用 request 参数创建一个新用户,然后返回用户实例。接着触发用户注册事件。
3. $this->guard()->login($user);
$this->guard()
'defaults' => [
'guard' => 'web', # 'passwords' => 'users',
],
'guards' => [
'web' => [
'driver' => 'session', Illuminate\Auth\SessionGuard::class
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
],
],
'providers' => [
'users' => [
'driver' => 'eloquent', Illuminate\Auth\EloquentUserProvider::class
'model' => App\User::class, #App\User::class
],
// 'users' => [
// 'driver' => 'database',
// 'table' => 'users',
// ],
],
'passwords' => [
'users' => [
'provider' => 'users',
'table' => 'password_resets',
'expire' => 60,
],
],
App\User
方法:
文件 vendor/laravel/framework/src/Illuminate/Auth/SessionGuard.php 内:
public function login(AuthenticatableContract $user, $remember = false)
{
$this->updateSession($user->getAuthIdentifier());
// If the user should be permanently "remembered" by the application we will
// queue a permanent cookie that contains the encrypted copy of the user
// identifier. We will then decrypt this later to retrieve the users.
if ($remember) {
$this->createRememberTokenIfDoesntExist($user); //如果用户选中了“记住我”,则生产remember_token
$this->queueRecallerCookie($user);
}
// If we have an event dispatcher instance set we will fire an event so that
// any listeners will hook into the authentication events and run actions
// based on the login and logout events fired from the guard instances.
$this->fireLoginEvent($user, $remember);
$this->setUser($user);
}
其中 $user->getAuthIdentifier() 用来获取用户唯一标识( Illuminate\Auth\Authenticatable::getAuthIdentifier)
其中 $this->updateSession(); 实现如下:
protected function updateSession($id)
{
$this->session->set($this->getName(), $id); //将用户唯一标识写入Session,记录登录状态
$this->session->migrate(true); //更新SessionID同时保留Session所有属性
}
4. return $this->registered($request, $user)
?: redirect($this->redirectPath());
public function redirectPath()
{
return property_exists($this, 'redirectTo') ? $this->redirectTo : '/home';
}
DRY 原则
登录(认证)
认证
$this->post('login', 'Auth\LoginController@login');
一, 登录(认证)
文件 vendor/laravel/framework/src/Illuminate/Foundation/Auth/AuthenticatesUsers.php 内:
public function login(Request $request)
{
$this->validateLogin($request); //数据验证
// If the class is using the ThrottlesLogins trait, we can automatically throttle
// the login attempts for this application. We'll key this by the username and
// the IP address of the client making these requests into this application.
if ($this->hasTooManyLoginAttempts($request)) { //爆破保护
$this->fireLockoutEvent($request);
return $this->sendLockoutResponse($request);
}
$credentials = $this->credentials($request); //获取登录凭证,这里指用email和密码的数组
if ($this->guard()->attempt($credentials, $request->has('remember'))) {
return $this->sendLoginResponse($request);
}
// If the login attempt was unsuccessful we will increment the number of attempts
// to login and redirect the user back to the login form. Of course, when this
// user surpasses their maximum number of attempts they will get locked out.
$this->incrementLoginAttempts($request);
return $this->sendFailedLoginResponse($request);
}
1. $this->validateLogin($request); 数据验证。
protected function validateLogin(Request $request)
{
$this->validate($request, [
$this->username() => 'required', 'password' => 'required',
]);
}
2. $credentials = $this->credentials($request);
protected function credentials(Request $request)
{
return $request->only($this->username(), 'password');
}
public function username()
{
return 'email';
}
3. $this->guard()->attempt($credentials, $request->has('remember')) //进行身份认证
这里 $this->guard() 获取系统默认 guard,配置文件 ),对应 Illuminate\Auth\SessionGuardIlluminate\Auth\SessionGuard
文件 vendor/laravel/framework/src/Illuminate/Auth/SessionGuard.php 内:
public function attempt(array $credentials = [], $remember = false, $login = true)
{
$this->fireAttemptEvent($credentials, $remember, $login); //触发认证事件
$this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials);
// If an implementation of UserInterface was returned, we'll ask the provider
// to validate the user against the given credentials, and if they are in
// fact valid we'll log the users into the application and return true.
if ($this->hasValidCredentials($user, $credentials)) {
if ($login) {
$this->login($user, $remember);
}
return true;
}
// If the authentication attempt fails we will fire an event so that the user
// may be notified of any suspicious attempts to access their account from
// an unrecognized user. A developer may listen to this event as needed.
if ($login) {
$this->fireFailedEvent($user, $credentials);
}
return false;
}
3.1 $this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials);
这里 $this->provider 是指 Illuminate\Auth\EloquentUserProvider::class 的实例。
文件 vendor/laravel/framework/src/Illuminate/Auth/EloquentUserProvider.php 内:
// 根据认证凭证去查询用户信息,返回 User Model 供 guard 实例进行身份认证
public function retrieveByCredentials(array $credentials)
{
if (empty($credentials)) {//这里的$credentials是指 email和明文密码的数组
return;
}
// First we will add each credential element to the query as a where clause.
// Then we can execute the query and, if we found a user, return it in a
// Eloquent User "model" that will be utilized by the Guard instances.
$query = $this->createModel()->newQuery();
foreach ($credentials as $key => $value) {
if (! Str::contains($key, 'password')) {
$query->where($key, $value);
}
}
return $query->first();
}
3.2 $this->hasValidCredentials($user, $credentials) 验证密码是否正确!!!
文件 vendor/laravel/framework/src/Illuminate/Auth/SessionGuard.php 内:
protected function hasValidCredentials($user, $credentials)
{
return ! is_null($user) && $this->provider->validateCredentials($user, $credentials);
}
文件 vendor/laravel/framework/src/Illuminate/Auth/EloquentUserProvider.php 内:
public function validateCredentials(UserContract $user, array $credentials)
{
$plain = $credentials['password'];
// $plain 明文密码, $user->getAuthPassword() 数据库内保持的hashed 密码
return $this->hasher->check($plain, $user->getAuthPassword());
}
Illuminate\Contracts\Hashing\Hasher 接口内:
/**
* Check the given plain value against a hash.
*
* @param string $value
* @param string $hashedValue
* @param array $options
* @return bool
*/
public function check($value, $hashedValue, array $options = []);
3.3 $this->login($user, $remember); 见注册部分的说明
退出登录
文件 vendor/laravel/framework/src/Illuminate/Foundation/Auth/AuthenticatesUsers.php 内:
public function logout(Request $request)
{
$this->guard()->logout();
$request->session()->flush();
$request->session()->regenerate();
return redirect('/');
}
文件 vendor/laravel/framework/src/Illuminate/Auth/SessionGuard.php 内:
public function logout()
{
$user = $this->user(); // User Model
// If we have an event dispatcher instance, we can fire off the logout event
// so any further processing can be done. This allows the developer to be
// listening for anytime a user signs out of this application manually.
$this->clearUserDataFromStorage(); //清除cookie, session
if (! is_null($this->user)) {
$this->refreshRememberToken($user); //清除数据库中的 remember_token
}
if (isset($this->events)) {
$this->events->fire(new Events\Logout($user));
}
// Once we have fired the logout event we will clear the users out of memory
// so they are no longer available as the user is no longer considered as
// being signed into this application and should not be available here.
$this->user = null;
$this->loggedOut = true;
}
protected function clearUserDataFromStorage()
{
$this->session->remove($this->getName()); //移除session
if (! is_null($this->getRecaller())) {
$recaller = $this->getRecallerName();
$this->getCookieJar()->queue($this->getCookieJar()->forget($recaller)); //cookie过期
}
}
/*清除 remember_token*/
protected function refreshRememberToken(AuthenticatableContract $user)
{
$user->setRememberToken($token = Str::random(60));
$this->provider->updateRememberToken($user, $token); //通过设置一个随机串来清除原 token
}
文件 vendor/laravel/framework/src/Illuminate/Auth/EloquentUserProvider.php 内:
/* Update the "remember me" token for the given user in storage. */
public function updateRememberToken(UserContract $user, $token)
{
$user->setRememberToken($token);
$user->save(); //更新数据库
}