homepagePHP/docs/Auth.md

400 lines
13 KiB
Markdown
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

### 重置RBAC用户表和用户与用户组关联表
#### 用法
+ config.php配置INJECT_RBAC
```blade
// key为标识字段用户登录后存入session中
// user为RBAC对应的用户数据表程序使用D函数
// role_user为用户与用户组关联数据表程序使用原生sql
'INJECT_RBAC' => [
['key' => 'PUBLIC_USER_LOGIN_ID', 'user' => 'User', 'role_user' => 'qs_role_user']
]
```
+ 用户登录后使用cleanRbacKey清空key的session值再重置对应key的session值
+ 用户登出后使用cleanRbacKey清空key的session值
### 权限过滤机制
不同用户一般只能看到与自己相关的数据该机制可以限制后台用户访问数据的权限不用针对不同用户分别处理where表达式降低开发难度。
#### 用法
+ 用户登录成功后设置AUTH_RULE_ID的session值
+ 配置对应Model类的$_auth_ref_rule若查询数据表存在别名自动处理auth_ref_key为“别名.字段名”)
如某机构(全思小伙伴)只能查看其创建书库点的书箱,则:
```php
// auth_ref_key是与用户关联的字段即AUTH_RULE_ID的值
// ref_path是该字段有关的数据表及其对应字段
// 机构OrganizationModel的配置
protected $_auth_ref_rule = array(
'auth_ref_key' => 'id',
'ref_path' => 'Organization.id'
);
// 书库点LibraryModel的配置
protected $_auth_ref_rule = array(
'auth_ref_key' => 'org_id',
'ref_path' => 'Organization.id'
);
// 书箱BoxModel的配置
protected $_auth_ref_rule = array(
'auth_ref_key' => 'library_id',
'ref_path' => 'Library.id'
);
```
若不使用该机制则需要根据登录用户来处理where表达式会导致查询的层级越高代码量就越多
```php
// 不使用该机制机构查询书箱数据则需要根据登录用户获取org_id再根据org_id获取library_id再根据library_id获取书箱id最后根据书箱id找出书箱数据
if (session('?' . C('USER_AUTH_KEY')){
$org_id = D('OrganizationUser')->where(['id' => session(C('USER_AUTH_KEY'))])->getField('org_id');
!$org_id && $org_map['_string'] = "1=0";
$org_id && $library_ids = D('Library')->where(['org_id' => $org_id])->getField('id', true);
if ($library_ids){
$box_ids = D('Box')->where(['library_id'=>['IN',$library_ids]]) -> getField('id',true);
$box_ids && $org_map['id'] = ['IN', $box_ids];
!$box_ids && $org_map['_string'] = "1=0";
}else{
$org_map['_string'] = "1=0";
}
$org_map && $map = array_merge($map, $org_map);
}
D('Box')->where($map)->select();
```
使用该机制后,机构查询书箱数据:
```php
D('Box')->where($map)->select();
```
### 扩展权限过滤机制
如系统存在机构用户OrgUser与书库点管理员LibraryUser有书库点Library数据分别与他们关联org_id与id对应的LibraryModel应该这样配置
```php
// 对于OrgUser书库点LibraryModel的配置
protected $_auth_ref_rule = array(
'auth_ref_key' => 'org_id',
'ref_path' => 'Organization.id'
);
// 对于LibraryUser书库点LibraryModel的配置
protected $_auth_ref_rule = array(
'auth_ref_key' => 'id',
'ref_path' => 'Library.id'
);
```
显然之前的权限过滤机制不能满足需求。
当系统存在多种不同类型的用户,而这些用户与数据相关联的字段不一致,扩展后可以根据不同类型的用户配置不同的权限过滤。
#### 用法
+ 配置对应Model类的$_auth_ref_rule自定义不同用户类型的权限过滤
```php
// 机构OrganizationModel的配置
protected $_auth_ref_rule = array(
'auth_ref_key' => 'id',
'ref_path' => 'Organization.id'
);
// 用户类型org为机构
// 用户类型library为书库点管理员
// 书库点LibraryModel的配置
protected $_auth_ref_rule = array(
'org' => [
'auth_ref_key' => 'org_id',
'ref_path' => 'Organization.id'
],
'library' => [
'auth_ref_key' => 'id',
'ref_path' => 'Library.id'
]
);
```
+ 登录方法需先清空再设置“AUTH_RULE_ID”、“AUTH_ROLE_TYPE”对应的值
```php
// 用户类型为“library”的登录方法
public function libraryUserLogin($name,$pwd){
// 省略登录逻辑处理
……
……
……
// 登录成功后
if ($r !== false){
cleanRbacKey();
session('LIBRARY_USER_LOGIN_ID',$ent['id']);
session(C('USER_AUTH_KEY'), $ent['id']);
\Qscmf\Core\AuthChain::setAuthFilterKey($ent['library_id'], 'library');
session('ADMIN_LOGIN', true);
session('HOME_LOGIN', null);
sysLogs('书库点管理员后台登录');
return true;
}else{
return false;
}
}
// 用户类型为“org”的登录方法
public function adminLogin($user_name, $pwd){
// 省略登录逻辑处理
……
……
……
// 登录成功后
if (!$ent){
return false;
}
cleanRbacKey();
// 设置超级管理员权限
if ($ent['id'] == C('USER_AUTH_ADMINID')) {
session(C('ADMIN_AUTH_KEY'), true);
} else {
session(C('ADMIN_AUTH_KEY'), false);
}
session('ORG_USER_LOGIN_ID', $ent['id']);
session(C('USER_AUTH_KEY'), $ent['id']);
\Qscmf\Core\AuthChain::setAuthFilterKey($ent['company_id'], 'org');
session('ADMIN_LOGIN', true);
session('HOME_LOGIN', null);
sysLogs($ent['company_id'] ? '机构后台登录' : '平台用户后台登录');
return true;
}
```
+ 登出方法需要使用函数“cleanRbacKey”、“cleanAuthFilterKey”清空对应的值
```php
// 用户类型为“org”的登出方法
public function sso_out(){
if (isAdminLogin()) {
cleanRbacKey();
\Qscmf\Core\AuthChain::cleanAuthFilterKey();
session(C('ADMIN_AUTH_KEY'), null);
session(C('USER_AUTH_KEY'), null);
session('ADMIN_LOGIN', null);
sysLogs('后台登出');
}
}
// 用户类型为“library”的登出方法
public function libraryUserLogout(){
if (isAdminLogin()) {
cleanRbacKey();
\Qscmf\Core\AuthChain::cleanAuthFilterKey();
session(C('ADMIN_AUTH_KEY'), null);
session(C('USER_AUTH_KEY'), null);
session('ADMIN_LOGIN', null);
sysLogs('后台登出');
}
$this->redirect('Public/libraryUserLogin');
}
```
### 开启前台过滤机制
可以config文件中将 'FRONT_AUTH_FILTER' 设置为true
```blade
'FRONT_AUTH_FILTER'=>true,
```
### 权限过滤机制支持配置若不存在关联数据则取消过滤
不存在关联数据时,默认返回空。使用此功能可以实现用户存在关联数据则只能查看关联的数据,若不存在关联数据则可以查看所有数据。
#### 用法
+ 配置对应Model类属性$_auth_ref_rule的not_exists_then_ignore其值为true
```blade
该值应设置在需要获取的关联数据主表对应的Model类如用户UserModel类与地区AreaModel类关联需要获取地区的数据应在AreaModel类设置该值为true。
```
```php
// 设置该值为true
// AreaModel类的配置
protected $_auth_ref_rule = array(
'auth_ref_key' => 'id',
'ref_path' => 'UserArea.city_id',
'not_exists_then_ignore' => true
);
```
### 权限过滤机制支持使用回调函数修改关联数据
权限过滤的关联数据为回调函数的结果。
#### 用法
+ 定义回调函数
+ 配置对应Model类属性$_auth_ref_rule的auth_ref_value_callback
```blade
auth_ref_value_callback的值为索引数组。
数组第一个元素为被调用的回调函数,若为公共函数,则为字符串;若为某个类的方法,则为数组,如[类名,方法名]
数组之后的元素是要被传入回调函数的参数。
回调函数接收关联数据的参数位置没有硬性规定可以根据实际情况使用占位符__auth_ref_value__程序执行时会根据ref_path的设置获取关联字段的实际值传给回调函数注意该值为数组。
该值应设置在需要获取的关联数据主表对应的Model类如用户UserModel类与地区AreaModel类关联需要获取地区的数据应在AreaModel类设置该值。
```
```php
// AreaModel类的配置
protected $_auth_ref_rule = array(
'auth_ref_key' => 'id',
'ref_path' => 'UserArea.city_id',
'auth_ref_value_callback' => ['callback_fun_name','__auth_ref_value__','param2'],
);
```
#### 场景举例
##### 若关联数据为同类型的树状结构数据,例如省市区、商品的类目,关联数据应扩展为该节点的子树。
```blade
若用户A与省市区的关联数据为广东省、湖南省则该用户可以查看这些省份及其下属所有地区的数据
若用户B没有关联地区数据则该用户可以查看所有地区的数据。
```
```php
// 不使用权限过滤功能,则每次获取用户能查看的地区数据时,都需要过滤地区数据。
public function getArea(){
$user_id = D('User')->getLoginUserId();
$map = [];
D('Area')->genWhereByUid($user_id, $map, 'id');
$area = D('Area')->where($map)->select();
return $area;
}
// AreaModel类
// 根据uid获取可以查看的地区数据
public function genWhereByUid($uid, &$map, $field){
$city_id = D('UserArea')->where(['user_id'=>$uid])->getField('city_id', true);
$all_city_id = $city_id ? getAllAreaIdsWithMultiPids($city_id) : null;
if ($all_city_id){
$map[$field] = ['IN', $all_city_id];
}
}
```
```blade
若使用之前的权限过滤功能用户A只能查看广东省、湖南省的数据而属于广东省的广州市的数据会被过滤掉
用户B可以查看的数据为空。
以下是使用此功能的步骤与效果。
```
+ 定义回调函数getFullAreaIdsWithMultiPids实现根据多个地区数据返回这些地区及其下属所有地区的数据
```php
function getAllAreaIdsWithMultiPids($city_ids, $model = 'AreaV', $max_level = 3, $need_exist = true, $cache = ''){
// 根据多个地区id获取其下属的所有地区具体算法省略
$all_city_ids = [];
foreach ($city_ids as $v){
}
return $all_city_ids;
}
```
+ 配置对应Model类属性$_auth_ref_rule定义回调函数及其参数
```php
// 配置地区AreaModel类
// 方式一:使用公共函数作为回调函数
protected $_auth_ref_rule = array(
'auth_ref_key' => 'id',
'ref_path' => 'UserArea.city_id',
'auth_ref_value_callback' => ['getAllAreaIdsWithMultiPids','__auth_ref_value__','AreaV',3,false],
'not_exists_then_ignore' => true
);
// 方式二:使用某个类的方法作为回调函数
protected $_auth_ref_rule = array(
'auth_ref_key' => 'id',
'ref_path' => 'UserArea.city_id',
'auth_ref_value_callback' => [[FullAreaModel::class,'getAllAreaIdsWithMultiPids'],'__auth_ref_value__','AreaV',3,false],
'not_exists_then_ignore' => true
);
// 配置UserAreaModel类
protected $_auth_ref_rule = array(
'auth_ref_key' => 'user_id',
'ref_path' => 'UserArea.city_id'
);
```
```php
// 只需要正确配置权限链使用此功能就可以实现需求,不再需要额外代码去过滤用户的关联地区数据。
public function getArea(){
$area = D('Area')->select();
return $area;
}
```
### 自定义Session类用于处理权限过滤使用的标识值
```blade
若使用权限链功能就需要设置AUTH_RULE_ID、INJECT_RBAC等值这些标识值默认使用公共函数session管理。
但不是所有的系统都适用公共函数session例如在前后端分离模式的系统。
对于以上系统,可以通过\Qscmf\Core\AuthChain类的registerSessionCls方法注册自定义Session类处理标识值。
```
#### 用法
+ 定义Session类实现接口Qscmf/Core/Session/ISession
```blade
默认为\Qscmf\Core\Session\DefaultSession类使用公共函数session管理。
```
```php
class CusSession implements Session\ISession
{
public function set($key, $value)
{
session($key, $value);
}
public function get($key){
return session($key);
}
public function clear($key)
{
$this->set($key,null);
}
}
```
+ 在app_init行为中加入注册该Session类
```php
class AppInitBehavior extends \Think\Behavior{
public function run(&$parm){
// 其它逻辑省略...
AuthChain::registerSessionCls(CusSession::class);
}
}
```