1. 生成相关文件和配置

快速生成命令

php artisan make:auth

运行后,使用git查看有哪些文件变化

$ git status
# On branch master
# Changed but not updated:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified: routes/web.php
#
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
# app/Http/Controllers/HomeController.php
# resources/views/auth/
# resources/views/home.blade.php
# resources/views/layouts/
no changes added to commit (use "git add" and/or "git commit -a")

2. 分析路由文件

查看下路由文件,发现多了两条路由信息

<?php

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/ Route::get('/', function () {
return view('welcome');
}); //以下为新加的路由
Auth::routes(); Route::get('/home', 'HomeController@index')->name('home');

分析下Auth::routes()做了什么事

定位到Auth这个Facades,config/app.php中的aliases中有一条

'aliases' => [
'Auth' => Illuminate\Support\Facades\Auth::class,
// ...其他
],

打开Illuminate\Support\Facades\Auth.php后看到有个routes()方法

    /**
* Register the typical authentication routes for an application.
*
* @return void
*/
public static function routes()
{
static::$app->make('router')->auth();
}

定位到Illuminate\Routing\route.php,有个auth()方法,这个就是我们route\web.php路由文件中Autu::routes()最后能生成的路由。

    public function auth()
{
// Authentication Routes...
$this->get('login', 'Auth\LoginController@showLoginForm')->name('login');
$this->post('login', 'Auth\LoginController@login');
$this->post('logout', 'Auth\LoginController@logout')->name('logout'); // Registration Routes...
$this->get('register', 'Auth\RegisterController@showRegistrationForm')->name('register');
$this->post('register', 'Auth\RegisterController@register'); // Password Reset Routes...
$this->get('password/reset', 'Auth\ForgotPasswordController@showLinkRequestForm')->name('password.request');
$this->post('password/email', 'Auth\ForgotPasswordController@sendResetLinkEmail')->name('password.email');
$this->get('password/reset/{token}', 'Auth\ResetPasswordController@showResetForm')->name('password.reset');
$this->post('password/reset', 'Auth\ResetPasswordController@reset');
}

查看下生效的路由,和上面的对比下,相匹配

$ php artisan route:list
+--------+----------+------------------------+------------------+------------------------------------------------------------------------+--------------+
| Domain | Method | URI | Name | Action | Middleware |
+--------+----------+------------------------+------------------+------------------------------------------------------------------------+--------------+
| | GET|HEAD | / | | Closure | web |
| | GET|HEAD | api/user | | Closure | api,auth:api |
| | GET|HEAD | home | home | App\Http\Controllers\HomeController@index | web,auth |
| | GET|HEAD | login | login | App\Http\Controllers\Auth\LoginController@showLoginForm | web,guest |
| | POST | login | | App\Http\Controllers\Auth\LoginController@login | web,guest |
| | POST | logout | logout | App\Http\Controllers\Auth\LoginController@logout | web |
| | POST | password/email | password.email | App\Http\Controllers\Auth\ForgotPasswordController@sendResetLinkEmail | web,guest |
| | GET|HEAD | password/reset | password.request | App\Http\Controllers\Auth\ForgotPasswordController@showLinkRequestForm | web,guest |
| | POST | password/reset | | App\Http\Controllers\Auth\ResetPasswordController@reset | web,guest |
| | GET|HEAD | password/reset/{token} | password.reset | App\Http\Controllers\Auth\ResetPasswordController@showResetForm | web,guest |
| | GET|HEAD | register | register | App\Http\Controllers\Auth\RegisterController@showRegistrationForm | web,guest |
| | POST | register | | App\Http\Controllers\Auth\RegisterController@register | web,guest |
+--------+----------+------------------------+------------------+------------------------------------------------------------------------+--------------+

3. 以登陆开始为例,分析auth到底是怎么工作的

3.1 分析登录文件

定位文件 app/Http/Controllers/Auth/LoginController.php

<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers; # 1. 登录控制器
class LoginController extends Controller
{
/*
|--------------------------------------------------------------------------
| Login Controller
|--------------------------------------------------------------------------
|
| This controller handles authenticating users for the application and
| redirecting them to your home screen. The controller uses a trait
| to conveniently provide its functionality to your applications.
|
*/ use AuthenticatesUsers; /**
* Where to redirect users after login.
*
* @var string
*/
protected $redirectTo = '/home'; /**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest')->except('logout');
}
}

LoginController.php并没有登录方法,why?实际上这里使用了trait,如果你用过软删除应该记得

use SoftDeletes;

这里也是使用了trait

use AuthenticatesUsers;

打开Illuminate\Foundation\Auth\AuthenticatesUsers.php

<?php

namespace Illuminate\Foundation\Auth;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Validation\ValidationException; trait AuthenticatesUsers
{
use RedirectsUsers, ThrottlesLogins; /**
* Handle a login request to the application.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\Response|\Illuminate\Http\JsonResponse
*/
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);
} # 2. 这里调用了attemptLogin()
if ($this->attemptLogin($request)) {
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);
} /**
* Attempt to log the user into the application.
*
* @param \Illuminate\Http\Request $request
* @return bool
*/
protected function attemptLogin(Request $request)
{
# 3. 找到 guard()方法
# 这里的attemp()方法一会分析
return $this->guard()->attempt(
$this->credentials($request), $request->filled('remember')
);
} /**
* Get the guard to be used during authentication.
*
* @return \Illuminate\Contracts\Auth\StatefulGuard
*/
protected function guard()
{
# 4. 访问了Auth门面的guard()方法
return Auth::guard();
} // ...其它方法暂时省略 }

到这里我们知道login方法的结果 : 先实例化Auth::guard()实例,最终返回了这个实例的attempt()方法

3.2 分析门面Auth。

以下使用config/auth.php的原始默认配置进行分析

根据config/app.php里面的服务提供器和Facades, 最终定位到 Illuminate\Auth\AuthManager.php


/**
* Attempt to get the guard from the local cache.
*
* @param string $name
* @return \Illuminate\Contracts\Auth\Guard|\Illuminate\Contracts\Auth\StatefulGuard
*/
public function guard($name = null)
{
# 5. 获取看守器,如果guard()没有参数则使用默认设置
$name = $name ?: $this->getDefaultDriver(); # 7. $name= 'web' 定位到resolve()方法
return $this->guards[$name] ?? $this->guards[$name] = $this->resolve($name);
} /**
* Resolve the given guard.
*
* @param string $name
* @return \Illuminate\Contracts\Auth\Guard|\Illuminate\Contracts\Auth\StatefulGuard
*
* @throws \InvalidArgumentException
*/
protected function resolve($name)
{
#8. 获取配置
$config = $this->getConfig($name); if (is_null($config)) {
throw new InvalidArgumentException("Auth guard [{$name}] is not defined.");
} if (isset($this->customCreators[$config['driver']])) {
return $this->callCustomCreator($name, $config);
} # 10 .$config = ['driver' => 'session','provider' => 'users',]
# 拼接创建看守器的方法名 这里默认是 createSessionDriver
$driverMethod = 'create'.ucfirst($config['driver']).'Driver'; if (method_exists($this, $driverMethod)) {
# 11. 访问createSessionDriver()
return $this->{$driverMethod}($name, $config);
} throw new InvalidArgumentException("Auth guard driver [{$name}] is not defined.");
} /**
* Call a custom driver creator.
*
* @param string $name
* @param array $config
* @return mixed
*/
protected function callCustomCreator($name, array $config)
{
return $this->customCreators[$config['driver']]($this->app, $name, $config);
} /**
* Create a session based authentication guard.
*
* @param string $name
* @param array $config
* @return \Illuminate\Auth\SessionGuard
*/
public function createSessionDriver($name, $config)
{
# $name = 'session'
# $cofnig['provider'] = 'users' # 12. createUserProvider不在此文件,定位到最上面有使用trait
# 通过这句 use CreatesUserProviders; 定位
# 查看下面的Illuminate\Auth\CreatesUserProviders.php文件
$provider = $this->createUserProvider($config['provider'] ?? null); $guard = new SessionGuard($name, $provider, $this->app['session.store']); # 18. 到这里我们知道
# $provide 是EloquentUserProvider实例
# $guard 是 SessionGuard实例
# 在回头看第3步,Auth::guard()->attempt()
# 接下来看下面Illuminate\Auth\SessionGuard.php中的attempt() // When using the remember me functionality of the authentication services we
// will need to be set the encryption instance of the guard, which allows
// secure, encrypted cookie values to get generated for those cookies.
if (method_exists($guard, 'setCookieJar')) {
$guard->setCookieJar($this->app['cookie']);
} if (method_exists($guard, 'setDispatcher')) {
$guard->setDispatcher($this->app['events']);
} if (method_exists($guard, 'setRequest')) {
$guard->setRequest($this->app->refresh('request', $guard, 'setRequest'));
} return $guard;
} /**
* Create a token based authentication guard.
*
* @param string $name
* @param array $config
* @return \Illuminate\Auth\TokenGuard
*/
public function createTokenDriver($name, $config)
{
// The token guard implements a basic API token based guard implementation
// that takes an API token field from the request and matches it to the
// user in the database or another persistence layer where users are.
$guard = new TokenGuard(
$this->createUserProvider($config['provider'] ?? null),
$this->app['request']
); $this->app->refresh('request', $guard, 'setRequest'); return $guard;
} /**
* Get the guard configuration.
*
* @param string $name
* @return array
*/
protected function getConfig($name)
{
# 9. $name = 'web'
# 读取config/auth.php 中的guards数组中的{$name}值,
# 对应 'web' => ['driver' => 'session','provider' => 'users',],
return $this->app['config']["auth.guards.{$name}"];
} /**
* Get the default authentication driver name.
*
* @return string
*/
public function getDefaultDriver()
{
# 6. 读取config/auth.php 中的default数组中的guard值,默认是web
return $this->app['config']['auth.defaults.guard'];
} /**
* Set the default guard driver the factory should serve.
*
* @param string $name
* @return void
*/
public function shouldUse($name)
{
$name = $name ?: $this->getDefaultDriver(); $this->setDefaultDriver($name); $this->userResolver = function ($name = null) {
return $this->guard($name)->user();
};
} /**
* Set the default authentication driver name.
*
* @param string $name
* @return void
*/
public function setDefaultDriver($name)
{
$this->app['config']['auth.defaults.guard'] = $name;
} /**
* Register a new callback based request guard.
*
* @param string $driver
* @param callable $callback
* @return $this
*/
public function viaRequest($driver, callable $callback)
{
return $this->extend($driver, function () use ($callback) {
$guard = new RequestGuard($callback, $this->app['request'], $this->createUserProvider()); $this->app->refresh('request', $guard, 'setRequest'); return $guard;
});
} /**
* Get the user resolver callback.
*
* @return \Closure
*/
public function userResolver()
{
return $this->userResolver;
} /**
* Set the callback to be used to resolve users.
*
* @param \Closure $userResolver
* @return $this
*/
public function resolveUsersUsing(Closure $userResolver)
{
$this->userResolver = $userResolver; return $this;
} /**
* Register a custom driver creator Closure.
*
* @param string $driver
* @param \Closure $callback
* @return $this
*/
public function extend($driver, Closure $callback)
{
$this->customCreators[$driver] = $callback; return $this;
} /**
* Register a custom provider creator Closure.
*
* @param string $name
* @param \Closure $callback
* @return $this
*/
public function provider($name, Closure $callback)
{
$this->customProviderCreators[$name] = $callback; return $this;
} /**
* Dynamically call the default driver instance.
*
* @param string $method
* @param array $parameters
* @return mixed
*/
public function __call($method, $parameters)
{
return $this->guard()->{$method}(...$parameters);
}
}

Illuminate\Auth\CreatesUserProviders.php

<?php

namespace Illuminate\Auth;

use InvalidArgumentException;

trait CreatesUserProviders
{
/**
* The registered custom provider creators.
*
* @var array
*/
protected $customProviderCreators = []; /**
* Create the user provider implementation for the driver.
*
* @param string|null $provider
* @return \Illuminate\Contracts\Auth\UserProvider|null
*
* @throws \InvalidArgumentException
*/
public function createUserProvider($provider = null)
{
# $provider = 'users' # 13. 获取提供器的配置
if (is_null($config = $this->getProviderConfiguration($provider))) {
return;
} if (isset($this->customProviderCreators[$driver = ($config['driver'] ?? null)])) {
return call_user_func(
$this->customProviderCreators[$driver], $this->app, $config
);
} # 15. $config = ['driver' => 'eloquent','model' => App\User::class,]
# $driver ='eloquent'
switch ($driver) {
case 'database':
return $this->createDatabaseProvider($config);
case 'eloquent':
# 16. 调用createEloquentProvider方法
return $this->createEloquentProvider($config);
default:
throw new InvalidArgumentException(
"Authentication user provider [{$driver}] is not defined."
);
}
} /**
* Get the user provider configuration.
*
* @param string|null $provider
* @return array|null
*/
protected function getProviderConfiguration($provider)
{
# $provider = 'users' # 14. 读取config/auth.php 中的providers数组中的{$provider}值
# 最终对应 'users' => ['driver' => 'eloquent','model' => App\User::class,],
if ($provider = $provider ?: $this->getDefaultUserProvider()) {
return $this->app['config']['auth.providers.'.$provider];
}
} /**
* Create an instance of the database user provider.
*
* @param array $config
* @return \Illuminate\Auth\DatabaseUserProvider
*/
protected function createDatabaseProvider($config)
{
$connection = $this->app['db']->connection(); return new DatabaseUserProvider($connection, $this->app['hash'], $config['table']);
} /**
* Create an instance of the Eloquent user provider.
*
* @param array $config
* @return \Illuminate\Auth\EloquentUserProvider
*/
protected function createEloquentProvider($config)
{
# 17. 实例化EloquentUserProvider,
# 记住提供器最终得到了EloquentUserProvider实例
# 返回 12
return new EloquentUserProvider($this->app['hash'], $config['model']);
} /**
* Get the default user provider name.
*
* @return string
*/
public function getDefaultUserProvider()
{
return $this->app['config']['auth.defaults.provider'];
}
}

Illuminate\Auth\SessionGuard.php 摘取主要涉及方法

    public function attempt(array $credentials = [], $remember = false)
{
$this->fireAttemptEvent($credentials, $remember); $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)) {
# 19. 调用hasValidCredentials()
# 22. 如果返回真 定位到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.
$this->fireFailedEvent($user, $credentials); return false;
} /**
* Determine if the user matches the credentials.
*
* @param mixed $user
* @param array $credentials
* @return bool
*/
protected function hasValidCredentials($user, $credentials)
{
# 20. 我们已经分析过$this->provider,默认得到的是EloquentUserProvider实例
# 查看下面文件 Illuminate\Auth\EloquentUserProvider.php
return ! is_null($user) && $this->provider->validateCredentials($user, $credentials);
} /**
* Log a user into the application.
*
* @param \Illuminate\Contracts\Auth\Authenticatable $user
* @param bool $remember
* @return void
*/
public function login(AuthenticatableContract $user, $remember = false)
{
# 23. 登录成功,存储session等操作。。。分析到此结束
$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->ensureRememberTokenIsSet($user); $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);
}

Illuminate\Auth\EloquentUserProvider.php 摘取主要涉及方法

    /**
* Validate a user against the given credentials.
*
* @param \Illuminate\Contracts\Auth\Authenticatable $user
* @param array $credentials
* @return bool
*/
public function validateCredentials(UserContract $user, array $credentials)
{
# 21. 最终这里进行了用户的验证,验证通过返回true,不通过返回false
$plain = $credentials['password']; return $this->hasher->check($plain, $user->getAuthPassword());
}

以上只是简单的分析了login的大体流程,感兴趣大家可以按照这个思路继续分析其他源码,有机会希望多交流,互相进步

laravel5.5用户认证源码分析的更多相关文章

  1. Django(64)频率认证源码分析与自定义频率认证

    前言 有时候我们发送手机验证码,会发现1分钟只能发送1次,这是做了频率限制,限制的时间次数,都由开发者自己决定 频率认证源码分析 def check_throttles(self, request): ...

  2. Django drf:序列化增删改查、局部与全局钩子源码流程、认证源码分析、执行流程

    一.序列化类的增.删.改.查 用drf的序列化组件   -定义一个类继承class BookSerializer(serializers.Serializer):   -写字段,如果不指定source ...

  3. Django-restframework 之认证源码分析

    Django-restframework 源码分析之认证 前言 最近学习了 django 的一个 restframework 框架,对于里面的执行流程产生了兴趣,经过昨天一晚上初步搞清楚了执行流程(部 ...

  4. drf认证源码分析

    补充: 一.django中间件之路由斜杠的自动添加源码 其实我们每次在写路由的时候,如果在路由层给他设置的是加/,但实际上我们在浏览器中输入的时候并没有加/发现也能调用,前面说了是浏览器内部走了重定向 ...

  5. Django-rest-framework源码分析----认证

    一.前言 1.1.安装 两种方式: github pip直接安装 pip install django-rest-framework 1.2.需要先了解的一些知识 理解下面两个知识点非常重要,djan ...

  6. Django rest framework源码分析(1)----认证

    目录 Django rest framework(1)----认证 Django rest framework(2)----权限 Django rest framework(3)----节流 Djan ...

  7. Django rest framework源码分析(一) 认证

    一.基础 最近正好有机会去写一些可视化的东西,就想着前后端分离,想使用django rest framework写一些,顺便复习一下django rest framework的知识,只是顺便哦,好吧. ...

  8. django身份认证、权限认证、频率校验使用及源码分析

    一. 身份认证源码分析 1.1 APIView源码的分析 APIView源码之前分析过https://www.cnblogs.com/maoruqiang/p/11135335.html,里面主要将r ...

  9. Django之REST framework源码分析

    前言: Django REST framework,是1个基于Django搭建 REST风格API的框架: 1.什么是API呢? API就是访问即可获取数据的url地址,下面是一个最简单的 Djang ...

随机推荐

  1. js 流程控制语句

    1.复合语句 2.switch语句 3.do...while语句 4.while语句 5.for语句 6.for...in语句 7.break和continue语句 9.with语句 10.if语句 ...

  2. 打表格,字符串处理,POJ(2136)

    题目链接:http://poj.org/problem?id=2136 水题WA了半天,结果是数组开小了. #include <stdio.h> #include <string.h ...

  3. 表面积最小(POJ3536)

    题目链接:http://poj.org/problem?id=3536 在体积固定的情况下,表面积最小时的长,宽,高. 这里枚举长,宽,根据体积计算高. #include <iostream&g ...

  4. 2017.9.24 JSP动态页面

    1.1 JSP(Java Server Page)是一种运行在服务器端的脚本语言,用来开发动态网页的开发技术. 1.2 JSP页面的结构 JSP页面主要由HTML和JSP代码构成,JSP代码是通过&q ...

  5. maven没有servlet(创建servlet后报错)

    maven不能创建servlet 解决方案 方案一 在项目的iml进行指定根目录 <sourceRoots> <root url="file://$MODULE_DIR$/ ...

  6. js延迟执行与循环执行

    延迟一段时间执行特定代码: setTimeout(function () { window.location.href = 'login' },1200); 循环执行: function test() ...

  7. java基础1.5版后新特性 自动装箱拆箱 Date SimpleDateFormat Calendar.getInstance()获得一个日历对象 抽象不要生成对象 get set add System.arrayCopy()用于集合等的扩容

    8种基本数据类型的8种包装类 byte Byte short Short int Integer long Long float Float double Double char Character ...

  8. 顺序语句:GOTO和NULL语句

    一 标号和GOTO 1 语法: PL/SQL中GOTO语句是无条件跳转到指定的标号去的意思.语法如下: GOTO label;......<<label>> /*标号是用< ...

  9. JavaScript中的事件循环

    JavaScript是单线程单并发语言 单线程:主程序只有一个线程,即同一时间片段内其只能执行单个任务. 引发的问题: 单线程,意味着任务都需要排队,前一个任务结束,才会执行后一个任务.若前一个任务耗 ...

  10. java后台输入数据的2种方式

    java后台输入数据的2种方式 (1) import java.io.BufferedReader; import java.io.InputStreamReader; public class 输入 ...