PS:onethink是基于该权限认证类实现,Auth类作为官方类库,在Library\Think里面。
其实Auth类也是基于角色访问控制RBAC扩展的,具体到节点的权限校验方式还是需要根据业务需求扩展。

Auth和RBAC

Rbac:

  1. Rbac是基于节点控制,根据3级节点,module,controller,action,节点类似与树形结构,3级节点间相互有关联
  2. 表关系:用户表->用户角色关联表->角色表->角色节点关联表->节点表
  3. 根据3级节点控制,粒度到操作action,每个节点为单一的模块,控制器或操作

Auth:

  1. 是对规则进行认证,不是对节点进行认证。用户可以把节点当作规则名称实现对节点进行认证。
  2. 表关系:用户表->用户和用户组关联表->用户组表->规则表
  3. 根据规则控制,可自由定制不同的规则,非常自由,同一个规则内可以定制多个不同节点(中间的关系:OR AND)
  4. 可定制规则表达式,比如定制积分表达式
  5. 一个用户可以属于多个用户组。我们需要设置每个用户组拥有哪些规则

RBAC是按节点进行认证的,如果要控制比节点更细的权限就有点困难了,比如页面上面的操作按钮, 我想判断用户权限来显示这个按钮, 如果没有权限就不会显示这个按钮; 再比如我想按积分进行权限认证, 积分在0-100时能干什么, 在101-200时能干什么。 这些权限认证用RABC都很困难。
而Auth权限认证, 它几乎是全能的, 除了能进行节点认证, 上面说的RABC很难认证的两种情况,它都能实现。

Auth的认证过程

数据库表介绍

用户表自行设计哟,提供uid即可

 //数据库
/*
-- ----------------------------
-- think_auth_rule,规则表,
-- id:主键,name:规则唯一标识, title:规则中文名称 status 状态:为1启用,为0禁用,condition:规则表达式,为空表示存在就验证,不为空表示按照条件验证
-- ----------------------------
DROP TABLE IF EXISTS `think_auth_rule`;
CREATE TABLE `think_auth_rule` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`name` char(80) NOT NULL DEFAULT '',
`title` char(20) NOT NULL DEFAULT '',
`type` tinyint(1) NOT NULL DEFAULT '1', #1-url,2-主菜单,这个字段可以根据业务逻辑,自己定义类别,见getAuthList函数
`status` tinyint(1) NOT NULL DEFAULT '1',
`condition` char(100) NOT NULL DEFAULT '', # 规则附件条件,满足附加条件的规则,才认为是有效的规则
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- ----------------------------
-- think_auth_group 用户组表,
-- id:主键, title:用户组中文名称, rules:用户组拥有的规则id, 多个规则","隔开,status 状态:为1启用,为0禁用
-- ----------------------------
DROP TABLE IF EXISTS `think_auth_group`;
CREATE TABLE `think_auth_group` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`title` char(100) NOT NULL DEFAULT '',
`status` tinyint(1) NOT NULL DEFAULT '1',#用户组状态:为1正常,为0禁用,-1为删除
`rules` char(80) NOT NULL DEFAULT '', #rule表的ID值,用逗号分隔,这样这个用户组就可以用多种规则了。
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- ----------------------------
-- think_auth_group_access 用户组明细表
-- uid:用户id,group_id:用户组id
-- ----------------------------
DROP TABLE IF EXISTS `think_auth_group_access`;
CREATE TABLE `think_auth_group_access` (
`uid` mediumint(8) unsigned NOT NULL,
`group_id` mediumint(8) unsigned NOT NULL,
UNIQUE KEY `uid_group_id` (`uid`,`group_id`),
KEY `uid` (`uid`),
KEY `group_id` (`group_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
*/

Thinkphp中Auth源码

仅仅160行~

 class Auth{

     //默认配置
protected $_config = array(
'AUTH_ON' => true, // 认证开关
'AUTH_TYPE' => 1, // 认证方式,1为实时认证;2为登录认证。
'AUTH_GROUP' => 'auth_group', // 用户组数据表名
'AUTH_GROUP_ACCESS' => 'auth_group_access', // 用户-用户组关系表
'AUTH_RULE' => 'auth_rule', // 权限规则表
'AUTH_USER' => 'member' // 用户信息表
); public function __construct() {
$prefix = C('DB_PREFIX');
$this->_config['AUTH_GROUP'] = $prefix.$this->_config['AUTH_GROUP'];
$this->_config['AUTH_RULE'] = $prefix.$this->_config['AUTH_RULE'];
$this->_config['AUTH_USER'] = $prefix.$this->_config['AUTH_USER'];
$this->_config['AUTH_GROUP_ACCESS'] = $prefix.$this->_config['AUTH_GROUP_ACCESS'];
if (C('AUTH_CONFIG')) {
//可设置配置项 AUTH_CONFIG, 此配置项为数组。
$this->_config = array_merge($this->_config, C('AUTH_CONFIG'));
}
} /**
* 检查权限
* @param name string|array 需要验证的规则列表,支持逗号分隔的权限规则或索引数组
* @param uid int 认证用户的id
* @param string mode 执行check的模式
* @param relation string 如果为 'or' 表示满足任一条规则即通过验证;如果为 'and'则表示需满足所有规则才能通过验证
* @return boolean 通过验证返回true;失败返回false
*/
public function check($name, $uid, $type=1, $mode='url', $relation='or') {
if (!$this->_config['AUTH_ON'])
return true;
//获取用户需要验证的所有有效规则列表
$authList = $this->getAuthList($uid,$type);
//是否验证多个规则,包含逗号,变成数组
if (is_string($name)) {
$name = strtolower($name);
if (strpos($name, ',') !== false) {
$name = explode(',', $name);
} else {
$name = array($name);
}
}
$list = array(); //保存验证通过的规则名
if ($mode=='url') {
//序列化,小写,又反序列化?
$REQUEST = unserialize( strtolower(serialize($_REQUEST)) );
}
foreach ( $authList as $auth ) {
//获取url的query参数
$query = preg_replace('/^.+\?/U','',$auth);
if ($mode=='url' && $query!=$auth ) {
//解析规则中的param,使其变成PHP变量
parse_str($query,$param);
//获取$REQUEST与$param带索引检查计算数组的交集
$intersect = array_intersect_assoc($REQUEST,$param);
//获取auth中的的query参数
$auth = preg_replace('/\?.*$/U','',$auth);
if ( in_array($auth,$name) && $intersect==$param ) { //如果节点相符且url参数满足
$list[] = $auth ;
}
}else if (in_array($auth , $name)){
//例如,$name='Admin/index',不带query参数,就不是url模式,那么就直接判断是否包含,否则如上代码判断url参数是否一致。
$list[] = $auth ;
}
}
if ($relation == 'or' and !empty($list)) {
return true;
}
$diff = array_diff($name, $list);
if ($relation == 'and' and empty($diff)) {
return true;
}
return false;
} /**
* 根据用户id获取用户组,返回值为数组
* @param uid int 用户id
* @return array 用户所属的用户组 array(
* array('uid'=>'用户id','group_id'=>'用户组id','title'=>'用户组名称','rules'=>'用户组拥有的规则id,多个,号隔开'),
* ...)
*/
public function getGroups($uid) {
static $groups = array();
if (isset($groups[$uid]))
return $groups[$uid];
$user_groups = M()
->table($this->_config['AUTH_GROUP_ACCESS'] . ' a')
->where("a.uid='$uid' and g.status='1'")
->join($this->_config['AUTH_GROUP']." g on a.group_id=g.id")
->field('uid,group_id,title,rules')->select();
$groups[$uid]=$user_groups?:array();
return $groups[$uid];
} /**
* 获得权限列表
* @param integer $uid 用户id
* @param integer $type
*/
protected function getAuthList($uid,$type) {
static $_authList = array(); //保存用户验证通过的权限列表
$t = implode(',',(array)$type);
if (isset($_authList[$uid.$t])) {
return $_authList[$uid.$t];
}
if( $this->_config['AUTH_TYPE']==2 && isset($_SESSION['_AUTH_LIST_'.$uid.$t])){
return $_SESSION['_AUTH_LIST_'.$uid.$t];
} //读取用户所属用户组,一个用户可以属于多个用户组
$groups = $this->getGroups($uid);
$ids = array();//保存用户所属用户组设置的所有权限规则id
foreach ($groups as $g) {
$ids = array_merge($ids, explode(',', trim($g['rules'], ',')));
}
$ids = array_unique($ids);
if (empty($ids)) {
$_authList[$uid.$t] = array();
return array();
} $map=array(
'id'=>array('in',$ids),
'type'=>$type,
'status'=>1,
);
//读取用户组所有权限规则
$rules = M()->table($this->_config['AUTH_RULE'])->where($map)->field('condition,name')->select(); //循环规则,判断结果。
$authList = array(); //
foreach ($rules as $rule) {
if (!empty($rule['condition'])) { //根据condition进行验证
$user = $this->getUserInfo($uid);//获取用户信息,一维数组
//使用用户的信息进行条件判断
//正则表达式替换,如定义{score}>5 and {score}<100 表示用户的分数在5-100之间时这条规则才会通过。
$command = preg_replace('/\{(\w*?)\}/', '$user[\'\\1\']', $rule['condition']);
//eval() 函数把字符串按照 PHP 代码来计算。
@(eval('$condition=(' . $command . ');'));
if ($condition) {
$authList[] = strtolower($rule['name']);
}
} else {
//只要存在就记录
$authList[] = strtolower($rule['name']);
}
}
$_authList[$uid.$t] = $authList;
if($this->_config['AUTH_TYPE']==2){
//如果是登录认证,规则列表结果保存到session,不是实时认证,就可以直接用session缓存了
$_SESSION['_AUTH_LIST_'.$uid.$t]=$authList;
}
return array_unique($authList);
} /**
* 获得用户资料,根据自己的情况读取数据库
*/
protected function getUserInfo($uid) {
static $userinfo=array();
if(!isset($userinfo[$uid])){
$userinfo[$uid]=M()->where(array('uid'=>$uid))->table($this->_config['AUTH_USER'])->find();
}
return $userinfo[$uid];
} }

实际使用

非常简单,就一个函数搞定
public function check($name, $uid, $type=1, $mode='url', $relation='or')

    /**
* 权限检测
* @param string $rule 检测的规则,例如Admin/Category/add,Admin/Category/edit,Admin/User/changeStatus?method=deleteUser
* @param string $mode check模式
* @return boolean
*/
final protected function checkRule($rule, $type=AuthRuleModel::RULE_URL, $mode='url'){
if(IS_ROOT){
return true;//管理员允许访问任何页面
}
static $Auth = null;
if (!$Auth) {
$Auth = new \Think\Auth();
}
//$rule = strtolower(MODULE_NAME.'/'.CONTROLLER_NAME.'/'.ACTION_NAME);
if(!$Auth->check($rule,UID,$type,$mode)){
return false;
}
return true;
}

参考

http://www.thinkphp.cn/topic/4029.html
http://www.thinkphp.cn/extend/675.html

最新文章

  1. jquery下拉菜单打开的同时,同行右边的图标变化
  2. HTTP 错误 500.19 - Internal Server Error 错误解决
  3. Ubuntu14.04安装MySQL-python异常: mysql_config: not found,Command &quot;python setup.py egg_info&quot; failed with error code 1 in /tmp/pip-build-MJWMPd/MySQL-python/
  4. 简单封装JDBC
  5. phalcon: 资源文件管 理 引入css,js
  6. 打饭助手之NABC
  7. 有联系的jQuery选择器
  8. HTML5 的新的表单属性
  9. Node.js 学习(二) 创建第一个应用
  10. HDU 5949 Relative atomic mass 【模拟】 (2016ACM/ICPC亚洲区沈阳站)
  11. android 32 Gallery:横着滚动的列表
  12. 【新建项目&amp;使用viewPager】实现一个Android电子书阅读APP
  13. FastDFS 分布式文件系统的安装与使用
  14. Django 模型中自定义Manager和模型方法
  15. jvm GC
  16. Python 下载win32api 模块
  17. Educational Codeforces Round 58 A,B,C,D,E,G
  18. .net 在同步方法中使用拉姆达表达式执行async/await异步操作
  19. python中time模块和datetime模块
  20. 【NIFI】 实现数据库到数据库之间数据同步

热门文章

  1. ORACLE-1:虚拟列影响alter修改表字段操作!
  2. Codeforces 900C. Remove Extra One(暴力)
  3. .net 空接合操作符 ??
  4. Java简单实现AOP,Java通用异常拦截,Java与Lamada
  5. C# 高性能对象映射(表达式树实现)
  6. strcmp返回值布尔类型的判断
  7. 一大波趣图:CSS的力量
  8. 可以避免的10大IT面试误区
  9. Win7 如何阻止程序联网
  10. SprimgMVC学习笔记(十)—— 拦截器