Laravel 用户认证 源码学习

本文介绍 Laravel 用户认证的使用、基本逻辑和底层的实现。

快速使用

在 Laravel 框架初始化后,运行 php artisan make:authphp artisan migrate 就能启用 Laravel 自带的用户认证功能。

  1. 用户认证的配置文件在 config/auth.php 中,
// 默认使用的配置。
// guard 是用户认证逻辑的实现。web 是网站用户认证逻辑;api 是 API 用户认证逻辑
'defaults' => [
        'guard' => 'web',
        'passwords' => 'users',
],

guard 还需配置对应的 driverprovider
driver 配置对应具体实现 guard 的逻辑类;provider 是逻辑类使用的数据提供者(查询、更新对应的用户信息的具体实现类)。

'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
        ...
    ],
  1. 认证的路由信息在 Illuminate\Routing\Router.php 中,通过 Illuminate\Support\Facades\Authroutes() 方法引入。注册和登录部分如下:
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...
 }

由路由信息可知,注册、登录对应的控制器和方法。

  1. 限制认证用户才能访问
    通常我们有一些接口是需要登录后才能访问的,在 Laravel 中是给路由分组,并添加中间件来实现。
// 请求到达路由中的 Controller 前,会先通过 auth 中间件验证
Route::middleware('auth')->group(function () {
    Route::get('/user', 'UserController@index');
    // ....
});

App\Http\Kernel 中定义了 auth 中间件

 protected $routeMiddleware = [
        'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
        // ....
]

注册、登录、登录检测逻辑

注册逻辑

注册逻辑在控制器 Auth\RegisterControllerregister 方法中,具体是由控制器中的 RegistersUsers trait 类实现。

public function register(Request $request)
{
     // 调用控制器中的方法对参数进行验证,包括 name\email\password
     $this->validator($request->all())->validate();
     // 调用控制器中 create 方法创建用户
     // 使用 $user 初始化 Redistered 对象
     // 通过 event() 函数触发注册事件
     event(new Registered($user = $this->create($request->all())));
      
     // 获取 guard 实例,并通过 guard 登录创建的用户
     $this->guard()->login($user);
     // 根据 $user 注册信息进行页面跳转
     return $this->registered($request, $user)
                        ?: redirect($this->redirectPath());
}

protected function guard()
{
    // 通过 Illuminate\Support\Facades\Auth 获取对应 guard 对象
    return Auth::guard();
}

登录逻辑

登录逻辑在控制器 Auth\LoginControllerlogin 方法中,具体是由控制器中的 AuthenticatesUsers trait 类实现。

public function login(Request $request)
{
   // 参数验证, email\password 参数
   $this->validateLogin($request);

   // 使用 ThrottlesLogins trait 对登录进行限制(一分钟内,登录失败超过配置的次数,将不能进行登录),防止恶意的登录尝试
   // 限制的依据是 登录的 email 和 IP 地址。
  
   if ($this->hasTooManyLoginAttempts($request)) {
        // 触发登录锁定事件
        $this->fireLockoutEvent($request);
        // 返回锁定的响应信息
        return $this->sendLockoutResponse($request);
   }
   // 根据 $request 中的登录凭证尝试登录
   // 这里实际以是获取 guard 对象进行登录尝试 
   if ($this->attemptLogin($request)) {
       // 登录成功后,重新生成 session,并跳转到设置的登录成功页面
       return $this->sendLoginResponse($request);
   }

   // 没登录成功,增加登录失败次数,返回登录失败的响应
   $this->incrementLoginAttempts($request);

   return $this->sendFailedLoginResponse($request);
}

protected function attemptLogin(Request $request)
{
    // 通过调用 Illuminate\Support\Facades\Auth 获取 guard 对象,并通过 guard 进行实际的登录逻辑
    return $this->guard()->attempt(
       $this->credentials($request), $request->has('remember')
    );
}

登录检测逻辑

中间件中通过 authenticate 方法检测用户是否登录

protected function authenticate(array $guards)
{  
    // 默认时,传递的 $guards 为空
    if (empty($guards)) {
        // 调用注入的 auth 对象的 authenticate 方法。auth 会调用默认 guard 的 authenticate 方法
        return $this->auth->authenticate();
    }

    foreach ($guards as $guard) {
        // 调用特定 guard 的 check 方法
        if ($this->auth->guard($guard)->check()) {
            return $this->auth->shouldUse($guard);
        }
    }

    throw new AuthenticationException('Unauthenticated.', $guards);
}

认证底层实现源码阅读

上面 Controller 中的登录和注册逻辑最终都是调用 Illuminate\Support\Facades\Auth 来获取 guard ,并通过 guard 来进行实际的登录、注册逻辑。
Illuminate\Support\Facades\Auth 是应用为 Illuminate\Auth\AuthManager 类提供的一个静态的接口,所以应用最终是使用 AuthManager 中的 guard() 方法来获取 guard 实例的。Facade 的原理可以参考文档。
下面为获取 guard 实例的主要逻辑:

public function guard($name = null)
{
    // 如果没有传 $name,就获取默认的 guard 。 默认的为配置中的 web
    $name = $name ?: $this->getDefaultDriver();
    // 如果 guards 已经被实例化的,就直接调用,否则通过 resolve 方法创建 guard 对象
    return isset($this->guards[$name])
                ? $this->guards[$name]
                : $this->guards[$name] = $this->resolve($name);
}

protected function resolve($name)
{
    // 获取名称对应的 guard 配置,前面说了,包括 driver\provider
    $config = $this->getConfig($name);
    // 配置异常判断
    if (is_null($config)) {
        throw new InvalidArgumentException("Auth guard [{$name}] is not defined.");
    }
    // 如果有设置自定义的 guard 创建方法,则用自定义的方法创建
    // 自定义的方法通过类中的 extend 方法定义
    if (isset($this->customCreators[$config['driver']])) {
        return $this->callCustomCreator($name, $config);
    }

    $driverMethod = 'create'.ucfirst($config['driver']).'Driver';
    // 根据配置,判断是否有对应创建 guard 的方法,有则调用
    // 系统自带 createSessionDriver\createTokenDriver 两个创建 guard 方法 
    if (method_exists($this, $driverMethod)) {
        return $this->{$driverMethod}($name, $config);
    }
    // 抛出 guard driver 没有定义的异常
    throw new InvalidArgumentException("Auth guard driver [{$name}] is not defined.");
}

默认情况下,web guard 的 driver 为 session。所以是通过 createSessionDriver 方法将会返回一个 SessionGuard 实例。然后通过 SessionGuard 实例进行用户认证逻辑。
这里最灵活的地方在于还提供了一个 extend 方法来自定义 guard。使得可以方便的扩展自己的用户认证。
下面是用户认证的相关的类图。

Laravel 用户认证 源码学习_第1张图片
用户认证类图.png

最后

有问题,欢迎留言交流。

参考文档

https://laravel-china.org/docs/laravel/5.4/authentication/1239

你可能感兴趣的:(Laravel 用户认证 源码学习)