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 相关文章推荐
php基础知识:类与对象(4) 范围解析操作符(::)
Dec 13 PHP
php jquery 实现新闻标签分类与无刷新分页
Dec 18 PHP
PHP,ASP.JAVA,JAVA代码格式化工具整理
Jun 15 PHP
PHP类中Static方法效率测试代码
Oct 17 PHP
php实例分享之mysql数据备份
May 19 PHP
函数中使用require_once问题深入探讨 优雅的配置文件定义方法推荐
Jul 02 PHP
php通过session防url攻击方法
Dec 10 PHP
smarty内置函数section的用法
Jan 22 PHP
基于OpenCart 开发支付宝,财付通,微信支付参数错误问题
Oct 01 PHP
Linux系统中设置多版本PHP共存配合Nginx服务器使用
Dec 21 PHP
PHP常用技巧汇总
Mar 04 PHP
php在windows环境下获得cpu内存实时使用率(推荐)
Feb 08 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
windows下PHP APACHE MYSQ完整配置
2007/01/02 PHP
PHP 全角转半角实现代码
2010/05/16 PHP
php版微信自动获取收货地址api用法示例
2016/09/22 PHP
CentOS系统中PHP安装扩展的方式汇总
2017/04/09 PHP
php实现的统计字数函数定义与使用示例
2017/07/26 PHP
基于IE下ul li 互相嵌套时的bug,排查,解决过程以及心得介绍
2013/05/07 Javascript
用JQuery实现全选与取消的两种简单方法
2014/02/22 Javascript
判断iframe里的页面是否加载完成
2014/06/06 Javascript
jquery实现兼容IE8的异步上传文件
2015/06/15 Javascript
jQuery采用连缀写法实现的折叠菜单效果
2015/09/18 Javascript
javascript日期格式化方法汇总
2015/10/04 Javascript
jQuery Dialog对话框事件用法实例分析
2016/05/10 Javascript
微信小程序 网络请求(post请求,get请求)
2017/01/17 Javascript
Vue实现virtual-dom的原理简析
2017/07/10 Javascript
JS写谷歌浏览器chrome的外挂实例
2018/01/11 Javascript
vue+element-ui+ajax实现一个表格的实例
2018/03/09 Javascript
详解js访问对象的属性和方法
2018/10/25 Javascript
vue中如何自定义右键菜单详解
2020/12/08 Vue.js
[01:01:51]EG vs VG Supermajor小组赛B组 BO3 第二场 6.2
2018/06/03 DOTA
简单介绍Python2.x版本中的cmp()方法的使用
2015/05/20 Python
使用pdb模块调试Python程序实例
2015/06/02 Python
Python使用urllib2模块抓取HTML页面资源的实例分享
2016/05/03 Python
Python 常用的安装Module方式汇总
2017/05/06 Python
利用pyinstaller打包exe文件的基本教程
2019/05/02 Python
Python使用LDAP做用户认证的方法
2019/06/20 Python
详解python编译器和解释器的区别
2019/06/24 Python
pytorch中的自定义反向传播,求导实例
2020/01/06 Python
Python 日期的转换及计算的具体使用详解
2020/01/16 Python
CSS3 实现弹幕的示例代码
2017/08/07 HTML / CSS
html5 localStorage本地存储_动力节点Java学院整理
2017/07/06 HTML / CSS
canvas仿写贝塞尔曲线的示例代码
2017/12/29 HTML / CSS
伦敦的高级牛仔布专家:Trilogy
2018/08/06 全球购物
吉列剃须刀英国官网:Gillette英国
2019/03/28 全球购物
全民健身日活动方案
2014/01/29 职场文书
营销总监岗位职责
2014/09/16 职场文书
python使用glob检索文件的操作
2021/05/20 Python