Laravel中的Auth模块详解


Posted in PHP onAugust 17, 2017

前言

本文主要给大家介绍的是关于Laravel中Auth模块的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧。

本文是基于Laravel 5.4 版本的本地化模块代码进行分析书写;

模块组成

Auth模块从功能上分为用户认证和权限管理两个部分;从文件组成上,Illuminate\Auth\Passwords目录下是密码重置或忘记密码处理的小模块,Illuminate\Auth是负责用户认证和权限管理的模块,Illuminate\Foundation\Auth提供了登录、修改密码、重置密码等一系统列具体逻辑实现;

下图展示了Auth模块各个文件的关系,并进行简要说明;

Laravel中的Auth模块详解

用户认证

HTTP本身是无状态,通常在系统交互的过程中,使用账号或者Token标识来确定认证用户;

配置文件解读

return [
 'defaults' => [
 'guard' => 'web',
 ...
 ],
 'guards' => [ 
 'web' => [
  'driver' => 'session',
  'provider' => 'users',
 ],
 'api' => [ 
  'driver' => 'token', 
  'provider' => 'users',
 ],
 ],
 'providers' => [
 'users' => [
  'driver' => 'eloquent',
  'model' => App\User::class,
 ], 
 ],
], 
];

从下往上,理解;

  • providers是提供用户数据的接口,要标注驱动对象和目标对象;此处,键名users是一套provider的名字,采用eloquent驱动,modal是App\User::class;
  • guards部分针对认证管理部分进行配置;有两种认证方式,一种叫web,还有一种是api;web认证是基于Session交互,根据sessionId获取用户id,在users这个provider查询出此用户;api认证是基于token值交互,也采用users这个provider;
  • defaults项显示默认使用web认证;

认证

Session绑定认证信息:

// $credentials数组存放认证条件,比如邮箱或者用户名、密码
// $remember 表示是否要记住,生成 `remember_token`
public function attempt(array $credentials = [], $remember = false) 
 
public function login(AuthenticatableContract $user, $remember = false)
 
public function loginUsingId($id, $remember = false)

HTTP基本认证,认证信息放在请求头部;后面的请求访问通过sessionId;

public function basic($field = 'email', $extraConditions = [])

只在当前会话中认证,session中不记录认证信息:

public function once(array $credentials = [])

public function onceUsingId($id)

public function onceBasic($field = 'email', $extraConditions = [])

认证过程中(包括注册、忘记密码),定义的事件有这些:

事件名 描述
Attempting 尝试验证事件
Authenticated 验证通过事件
Failed 验证失败事件
Lockout 失败次数超过限制,锁住该请求再次访问事件
Logi 通过‘remember_token'成功登录时,调用的事件
Logout 用户退出事件
Registered 用户注册事件

还有一些其他的认证方法:

  • 检查是否存在认证用户:Auth::check()
  • 获取当前认证用户:Auth::user()
  • 退出系统:Auth::logout()

密码处理

配置解读

return [
 'defaults' => [
  'passwords' => 'users',
  ...
 ],
 
 'passwords' => [
  'users' => [
   'provider' => 'users',
   'table' => 'password_resets',
   'expire' => 60,
  ],
 ],
]

从下往上,看配置;

  • passwords数组是重置密码的配置;users是配置方案的别名,包含三个元素:provider(提供用户的方案,是上面providers数组)、table(存放重置密码token的表)、expire(token过期时间)
  • default 项会设置默认的 passwords 重置方案;

重置密码的调用与实现

先看看Laravel的重置密码功能是怎么实现的:

public function reset(array $credentials, Closure $callback) {
 // 验证用户名、密码和 token 是否有效
 $user = $this->validateReset($credentials);

 if (! $user instanceof CanResetPasswordContract) {
   return $user;
 } 
 
 $password = $credentials['password'];
 // 回调函数执行修改密码,及持久化存储
 $callback($user, $password);
 // 删除重置密码时持久化存储保存的 token
 $this->tokens->delete($user);

 return static::PASSWORD_RESET;
}

再看看Foundation\Auth模块封装的重置密码模块是怎么调用的:

// 暴露的重置密码 API
public function reset(Request $request) {
 // 验证请求参数 token、email、password、password_confirmation
 $this->validate($request, $this->rules(), $this->validationErrorMessages());
 // 调用重置密码的方法,第二个参数是回调,做一些持久化存储工作
 $response = $this->broker()->reset(
  $this->credentials($request), function ($user, $password) {
  $this->resetPassword($user, $password);
  }
 );
 // 封装 Response
 return $response == Password::PASSWORD_RESET
  ? $this->sendResetResponse($response)
  : $this->sendResetFailedResponse($request, $response);
}

// 获取重置密码时的请求参数
protected function credentials(Request $request) {
 return $request->only(
  'email', 'password', 'password_confirmation', 'token'
 );
}

// 重置密码的真实性验证后,进行的持久化工作
protected function resetPassword($user, $password) {
 // 修改后的密码、重新生成 remember_token
 $user->forceFill([
  'password' => bcrypt($password),
  'remember_token' => Str::random(60),
 ])->save();
 // session 中的用户信息也进行重新赋值          
 $this->guard()->login($user);
}

“忘记密码 => 发邮件 => 重置密码” 的大体流程如下:

  • 点击“忘记密码”,通过路由配置,跳到“忘记密码”页面,页面上有“要发送的邮箱”这个字段要填写;
  • 验证“要发送的邮箱”是否是数据库中存在的,如果存在,即向该邮箱发送重置密码邮件;
  • 重置密码邮件中有一个链接(点击后会携带 token 到修改密码页面),同时数据库会保存这个 token 的哈希加密后的值;
  • 填写“邮箱”,“密码”,“确认密码”三个字段后,携带 token 访问重置密码API,首页判断邮箱、密码、确认密码这三个字段,然后验证 token是否有效;如果是,则重置成功;

权限管理

权限管理是依靠内存空间维护的一个数组变量abilities来维护,结构如下:

$abilities = array(
 '定义的动作名,比如以路由的 as 名(common.dashboard.list)' => function($user) {
  // 方法的参数,第一位是 $user, 当前 user, 后面的参数可以自行决定
  return true; // 返回 true 意味有权限, false 意味没有权限
 },
 ......
);

但只用 $abilities,会使用定义的那部分代码集中在一起太烦索,所以有policy策略类的出现;

policy策略类定义一组实体及实体权限类的对应关系,比如以文章举例:

有一个 Modal实体类叫 Post,可以为这个实体类定义一个PostPolicy权限类,在这个权限类定义一些动作为方法名;

class PostPolicy {
 // update 权限,文章作者才可以修改
 public function update(User $user, Post $post) {
  return $user->id === $post->user_id;
 }
}

然后在ServiceProvider中注册,这样系统就知道,如果你要检查的类是Post对象,加上你给的动作名,系统会找到PostPolicy类的对应方法;

protected $policies = [
 Post::class => PostPolicy::class,
];

怎么调用呢?

对于定义在abilities数组的权限:

  • 当前用户是否具备common.dashboard.list权限:Gate::allows('common.dashboard.list')
  • 当前用户是否具备common.dashboard.list权限:! Gate::denies('common.dashboard.list')
  • 当前用户是否具备common.dashboard.list权限:$request->user()->can('common.dashboard.list')
  • 当前用户是否具备common.dashboard.list权限:! $request->user()->cannot('common.dashboard.list')
  • 指定用户是否具备common.dashboard.list权限:Gate::forUser($user)->allows('common.dashboard.list')

对于policy策略类调用的权限:

  • 当前用户是否可以修改文章(Gate 调用):Gate::allows('update', $post)
  • 当前用户是否可以修改文章(user 调用):$user->can('update', $post)
  • 当前用户是否可以修改文章(用帮助函数):policy($post)->update($user, $post)
  • 当前用户是否可以修改文章(Controller 类方法中调用):$this->authorize('update', $post);
  • 当前用户是否可以修改文章(Controller 类同名方法中调用):$this->authorize($post);
  • 指定用户是否可以修改文章(Controller 类方法中调用):$this->authorizeForUser($user, 'update', $post);

有用的技巧

获取当前系统注册的权限,包括两部分abilities和policies数组内容,代码如下:

$gate = app(\Illuminate\Contracts\Auth\Access\Gate::class);
$reflection_gate = new ReflectionClass($gate);

$policies = $reflection_gate->getProperty('policies');
$policies->setAccessible(true);
// 获取当前注册的 policies 数组
dump($policies->getValue($gate));
                          
$abilities = $reflection_gate->getProperty('abilities');          
$abilities->setAccessible(true);
// 获取当前注册的 abilities 数组
dump($abilities->getValue($gate));

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

PHP 相关文章推荐
第八节--访问方式
Nov 16 PHP
php通过文件头检测文件类型通用代码类(zip,rar等)
Oct 19 PHP
PHPMYADMIN导入数据最大为2M的解决方法
Apr 23 PHP
Drupal7 form表单二次开发要点与实例
Mar 02 PHP
PHP使用xmllint命令处理xml与html的方法
Dec 15 PHP
phpcms手机内容页面添加上一篇和下一篇
Jun 05 PHP
php实现统计目录文件大小的函数
Dec 25 PHP
Yii2 rbac权限控制操作步骤实例教程
Apr 29 PHP
PHP简单读取PDF页数的实现方法
Jul 21 PHP
PHP生成唯一ID之SnowFlake算法
Dec 17 PHP
tp5(thinkPHP5)框架数据库Db增删改查常见操作总结
Jan 10 PHP
tp5框架无刷新分页实现方法分析
Sep 26 PHP
PHP实现基于回溯法求解迷宫问题的方法详解
Aug 17 #PHP
PHP基于Closure类创建匿名函数的方法详解
Aug 17 #PHP
PHP编译configure时常见错误的总结
Aug 17 #PHP
基于PHP常用文件函数和目录函数整理
Aug 17 #PHP
PHP实现的堆排序算法详解
Aug 17 #PHP
基于php编程规范(详解)
Aug 17 #PHP
PHP数据库操作四:mongodb用法分析
Aug 16 #PHP
You might like
php chr() ord()中文截取乱码问题解决方法
2008/09/08 PHP
PHP curl 抓取AJAX异步内容示例
2014/09/09 PHP
PHP开发Apache服务器配置
2015/07/15 PHP
php验证码实现代码(3种)
2015/09/07 PHP
详解WordPress中用于更新和获取用户选项数据的PHP函数
2016/03/08 PHP
laravel中的错误与日志用法详解
2016/07/26 PHP
thinkphp jquery实现图片上传和预览效果
2020/07/22 PHP
通过源码解析Laravel的依赖注入
2018/01/22 PHP
php多进程并发编程防止出现僵尸进程的方法分析
2020/02/28 PHP
Javascript下的keyCode键码值表
2007/04/10 Javascript
使用jQuery实现星级评分代码分享
2014/12/09 Javascript
JavaScript中rem布局在react中的应用
2015/12/09 Javascript
使用Javascript实现选择下拉菜单互移并排序
2016/02/23 Javascript
Bootstrap禁用响应式布局的实现方法
2017/03/09 Javascript
详解封装基础的angular4的request请求方法
2018/06/05 Javascript
koa-router源码学习小结
2018/09/07 Javascript
jquery拖拽自动排序插件使用方法详解
2020/07/20 jQuery
在Pycharm中项目解释器与环境变量的设置方法
2018/10/29 Python
Numpy中对向量、矩阵的使用详解
2019/10/29 Python
python实现简单学生信息管理系统
2020/04/09 Python
css3 线性渐变和径向渐变示例附图
2014/04/08 HTML / CSS
css3设置box-pack和box-align让div里面的元素垂直居中
2014/09/01 HTML / CSS
ORACLE第二个十问
2013/12/14 面试题
直接有效的自我评价
2014/01/11 职场文书
优秀管理者获奖感言
2014/02/17 职场文书
大学军训感言800字
2014/02/27 职场文书
高校教师岗位职责
2014/03/18 职场文书
平安工地建设方案
2014/05/06 职场文书
党的群众路线教育实践活动个人承诺书
2014/05/22 职场文书
办公室文员岗位职责范本
2014/06/12 职场文书
司机工作自我鉴定
2014/09/19 职场文书
学生检讨书如何写
2014/10/30 职场文书
幼儿园教师自我评价
2015/03/04 职场文书
关于教师节的广播稿
2015/08/19 职场文书
在前女友婚礼上,用Python破解了现场的WIFI还把名称改成了
2021/05/28 Python
4种方法python批量修改替换列表中元素
2022/04/07 Python