视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
关于Laravel Auth原理浅析
2020-11-02 18:29:59 责编:小采
文档
 下面由Laravel教程栏目给大家介绍Laravel Auth原理浅析,希望对需要的朋友有所帮助!

由于公司最近使用Laravel-admin做后台,接触了下Laravel框架,不得不说,Laravel社区的力量以及生态确实挺强大。

  但是公司内部业务都处于Java端,后台全部都是调JavaApi,因此使用Laravel的特性就得大打折扣了,首先Eloquent模型完全不能用,我这边把业务分开来,只存了3张表,这是Laravel-admin自带的表。

  Laravel-admin带了9张表,由于用户登录业务全保存在Api端,自带的表功能被我割舍了。因此需要自己实现Api登录的逻辑,而又必须走Laravel Auth认证。

原理解读

// 使用下面这个命令Laravel会自动为我们生成Auth路由和认证模块。跟着代码往下解读。
 php artisan make:auth 
 
// Http/Controllers/Auth/LoginController 使用了 AuthenticatesUsers

其中 下面这三个方法诠释了登录逻辑的全部。

public function login(Request $request)
 {
 $this->validateLogin($request);
 if ($this->hasTooManyLoginAttempts($request)) {
 $this->fireLockoutEvent($request);
 return $this->sendLockoutResponse($request);
 }
 // 这里尝试登录系统,
 if ($this->attemptLogin($request)) {
 return $this->sendLoginResponse($request);
 }
 $this->incrementLoginAttempts($request);
 return $this->sendFailedLoginResponse($request);
 }
 protected function attemptLogin(Request $request)
 {
 return $this->guard()->attempt(
 $this->credentials($request), $request->has('remember')
 );
 }
 protected function guard()
 {
 return Auth::guard();
 }

控制器会去寻找Auth::guard(), 那这个Auth::guard()是个什么东西呢,

首先 Auth 是系统的单例,原型在

IlluminateAuthAuthManager;

顾名思义,是一个Auth管理模块,实现了认证工厂模式接口guards(),

public function __construct($app)
 {
 $this->app = $app;
 $this->userResolver = function ($guard = null) {
 return $this->guard($guard)->user();
 };
 }
 // Auth::guard();就是调用了这个方法。
 public function guard($name = null)
 {
 // 首先查找$name, 没有就使用默认的驱动,
 $name = $name ?: $this->getDefaultDriver();
 // 意思就是要实例化出这个驱动并且返回,
 return isset($this->guards[$name])
 ? $this->guards[$name]
 : $this->guards[$name] = $this->resolve($name);
 }
   
 // 默认的驱动是从配置文件里面读取的,/config/auth.php default配置项
 public function getDefaultDriver()
 {
 return $this->app['config']['auth.defaults.guard'];
 }
  
   // 这里是构造Auth-guard驱动
   protected function resolve($name)
 {
 $config = $this->getConfig($name);
 if (is_null($config)) {
 throw new InvalidArgumentException("xxx");
 }
 // 这里是如果你自己实现的驱动就返回
 if (isset($this->customCreators[$config['driver']])) {
 return $this->callCustomCreator($name, $config);
 }
 // 这里是系统默认两个类分别是 
 // session 和 token 这里主要讲 sessionGuard .
 $driverMethod = 'create'.ucfirst($config['driver']).'Driver';
 if (method_exists($this, $driverMethod)) {
 return $this->{$driverMethod}($name, $config);
 }
 throw new InvalidArgumentException("xxx");
 }

接下来看看配置文件 auth.php

 // Auth::guard() ,不传参数,就调用默认的default.guard ,
 'defaults' => [
 'guard' => 'web',
 'passwords' => 'users',
 ],
 // 系统的guard .默认支持 "database", "eloquent",意思就是说你的provider必须是这两个实例中的一个,
 'guards' => [
 'web' => [
 'driver' => 'session',
 'provider' => 'users',
 ],
 'api' => [
 'driver' => 'token',
 'provider' => 'users',
 ],
 ],
 // 这个就是上面的provider了,你使用哪一个provider作为你的Auth::guard()返回的
 // 模型
 'providers' => [
 'users' => [
 'driver' => 'eloquent',
 'model' => AppUser::class,
 ],
 // 'users' => [
 // 'driver' => 'database',
 // 'table' => 'users',
 // ],
 ],

也就是说终归到底,Auth::guard(), 在默认配置里面是给我反回了一个sessionGuard .

主要看下面4个方法

namespace IlluminateAuth;
class SessionGuard{
 public function attempt(array $credentials = [], $remember = false)
 {
 // 这里触发 试图登录事件,此时还没有登录
 $this->fireAttemptEvent($credentials, $remember);
 $this->lastAttempted = 
 $user = $this->provider->retrieveByCredentials($credentials);
 // 这里会调用hasValidCredentials,其实就是验证用户名和密码的一个过程
 if ($this->hasValidCredentials($user, $credentials)) {
 // 如果验证通过了,就调用login方法 .
 $this->login($user, $remember);
 return true;
 }
 // 否则就触发登录失败事件,返回假
 $this->fireFailedEvent($user, $credentials);
 return false;
 }
 // 这里是登录用户的操作,就是说调用这个方法已经是合法用户了,必须是一个
  // AuthenticatableContract 的实例 .
 public function login(AuthenticatableContract $user, 
 $remember = false)
 {
 // 直接更新session,这里就是把session存起来,session的键在该方法的
 // getName() 里边,
 $this->updateSession($user->getAuthIdentifier());
 if ($remember) {
 $this->ensureRememberTokenIsSet($user);
 $this->queueRecallerCookie($user);
 }
     // 触发登录事件,已经登录了这个时候,
 $this->fireLoginEvent($user, $remember);
 // 将user对象保存到sessionGuard , 后续的类访问Auth::user();直接拿到
 $this->setUser($user);
 }
 // 这里就是经常使用到的 Auth::user()了,具体如何返回看AuthManager里面的
 // __call
 public function user()
 {
 if ($this->loggedOut) {
 return;
 }
 if (! is_null($this->user)) {
 return $this->user;
 }
 // 这里读取session拿到user的id ,
 $id = $this->session->get($this->getName());
 $user = null;
 // 如果拿到了id ,查找到该user
 if (! is_null($id)) {
 if ($user = $this->provider->retrieveById($id)) {
 $this->fireAuthenticatedEvent($user);
 }
 }
 $recaller = $this->recaller();
 if (is_null($user) && ! is_null($recaller)) {
 $user = $this->userFromRecaller($recaller);
 if ($user) {
 $this->updateSession($user->getAuthIdentifier());
 $this->fireLoginEvent($user, true);
 }
 }
 return $this->user = $user;
 }
 // 这里就直接返回用户id了,
 public function id()
 {
 if ($this->loggedOut) {
 return;
 }
 return $this->user()
 ? $this->user()->getAuthIdentifier()
 : $this->session->get($this->getName());
 }
}

大体上用户登录的流程就完了,简单过程就是

//伪代码
$credentials = $request()->only(['username' ,'password']);
if(Auth::guard("session")->attempt($credentials)){
 // 登录成功
}else{
 // 登录失败
}

实现用户登录之后才能访问的控制器/方法

Route::get("/home")->middleware("auth");
// auth Middleware 是在app/Http/Kernel中注册的,
// 类名是 IlluminateAuthMiddlewareAuthenticate::class
// 解析过程实质上是这个方法:
 public function handle($request, Closure $next, ...$guards)
 {
 $this->authenticate($guards);
 return $next($request);
 }
 
 protected function authenticate(array $guards)
 { 
 // 默认情况下会去 Auth中寻找authenticate这个方法
 if (empty($guards)) {
 return $this->auth->authenticate();
 }
 // 如果middleware中传了参数,会遍历一遍,不通过就抛出异常
 foreach ($guards as $guard) {
 if ($this->auth->guard($guard)->check()) {
 return $this->auth->shouldUse($guard);
 }
 }
 throw new AuthenticationException('Unauthenticated.', $guards);
 }
 //sessionGuard 中的authenticate其实也就是调用了一遍user方法。
 public function authenticate()
 {
 if (! is_null($user = $this->user())) {
 return $user;
 }
 throw new AuthenticationException;
 }

第一次写文,有错误请指出谢谢!

下载本文
显示全文
专题