php中实现简单的ACL 完结篇


Posted in PHP onSeptember 07, 2011
-- ACL Tables 
-- 表的结构 `aclresources` 
DROP TABLE IF EXISTS `aclresources`; 
CREATE TABLE IF NOT EXISTS `aclresources` ( 
`rsid` varchar(64) NOT NULL , 
`access` int(4) NOT NULL default 0, 
`desc` varchar(240) NOT NULL default '', 
`created_at` int(10) unsigned NOT NULL default 1, 
`updated_at` int(10) unsigned NOT NULL default 0, 
PRIMARY KEY (`rsid`) 
)DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 
-- 表的结构 `aclroles` 
DROP TABLE IF EXISTS `aclroles`; 
CREATE TABLE IF NOT EXISTS `aclroles` ( 
`id` int(10) unsigned NOT NULL auto_increment, 
`rolename` varchar(32) NOT NULL , 
`desc` varchar(240) NOT NULL default '', 
`created_at` int(10) unsigned NOT NULL default 1, 
`updated_at` int(10) unsigned NOT NULL default 0, 
PRIMARY KEY (`id`), 
UNIQUE KEY `rolename` (`rolename`) 
)DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 
-- 表的结构 `ref_aclresources_aclroles` 
DROP TABLE IF EXISTS `ref_aclresources_aclroles`; 
CREATE TABLE IF NOT EXISTS `ref_aclresources_aclroles` ( 
`rsid` varchar(64) NOT NULL , 
`role_id` int(10) unsigned NOT NULL , 
PRIMARY KEY (`rsid`,`role_id`) 
)DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 
-- 表的结构 `ref_users_aclroles` 
DROP TABLE IF EXISTS `ref_users_aclroles`; 
CREATE TABLE IF NOT EXISTS `ref_users_aclroles` ( 
`user_id` int(10) unsigned NOT NULL auto_increment, 
`role_id` int(10) unsigned NOT NULL , 
PRIMARY KEY (`user_id`,`role_id`) 
)DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 
-- 表的结构 `users` 
DROP TABLE IF EXISTS `users`; 
CREATE TABLE `users` ( 
`id` int(10) unsigned NOT NULL auto_increment, 
`email` varchar(128) NOT NULL, 
`password` varchar(64) NOT NULL, 
`nickname` varchar(32) NOT NULL default '', 
`roles` varchar(240) NOT NULL default '', 
`created_at` int(10) unsigned NOT NULL default 1, 
`updated_at` int(10) unsigned NOT NULL default 0, 
PRIMARY KEY (`id`), 
UNIQUE KEY `user_email` (`email`) 
)DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

php 类
<?php 
/** 
* 简单的 ACL 权限控制功能 
* 
* 表定义 
* 
* 1. 资源定义 (rsid,access,desc,created_at,updated_at) 
* 2. 角色定义 (id,rolename,desc,created_at,updated_at) 
* 3. 资源-角色关联 (rsid,role_id) 
* 4. 用户-角色关联 (user_id,role_id) 
* 
* 依赖db.php sqlobject.php 
* 
* @author vb2005xu.iteye.com 
*/ 
class AclBase { 
// --- ACL 访问授权 /** 
* 不允许任何人访问 
*/ 
const NOBODY = 0; 
/** 
* 允许任何人访问 
*/ 
const EVERYONE = 1; 
/** 
* 允许 拥有角色的用户访问 
*/ 
const HAS_ROLE = 2; 
/** 
* 允许 不带有角色的用户访问 
*/ 
const NO_ROLE = 3; 
/** 
* 在 资源-角色关联 定义的 角色才能访问 
*/ 
const ALLOCATE_ROLES = 4; 
// 定义相关的 表名 
public $tbResources = 'aclresources'; 
public $tbRoles = 'aclroles'; 
public $tbRefResourcesRoles = 'ref_aclresources_aclroles'; 
public $tbRefUsersRoles = 'ref_users_aclroles'; 
/** 
* 格式化 资源的访问权限并返回 
* 
* @return int 
*/ 
static function formatAccessValue($access){ 
static $arr = array(self::NOBODY,self::EVERYONE,self::HAS_ROLE,self::NO_ROLE,self::ALLOCATE_ROLES); 
return in_array($access,$arr) ? $access : self::NOBODY; 
} 
/** 
* 创建资源,返回资源记录主键 
* 
* @param string $rsid 
* @param int $access 
* @param string $desc 
* 
* @return int 
*/ 
function createResource($rsid,$access,$desc){ 
if (empty($rsid)) return false; 
$resource = array( 
'rsid' => $rsid, 
'access' => self::formatAccessValue($access), 
'desc' => $desc, 
'created_at' => CURRENT_TIMESTAMP 
); 
return SingleTableCRUD::insert($this->tbResources,$resource); 
} 
/** 
* 修改资源,返回成功状态 
* 
* @param array $resource 
* @return int 
*/ 
function updateResource(array $resource){ 
if (!isset($resource['rsid'])) return false; 
$resource['updated_at'] = CURRENT_TIMESTAMP; 
return SingleTableCRUD::update($this->tbResources,$resource,'rsid'); 
} 
/** 
* 删除资源 
* 
* @param string $rsid 
* @return int 
*/ 
function deleteResource($rsid){ 
if (empty($rsid)) return false; 
return SingleTableCRUD::delete($this->tbResources,array('rsid'=>$rsid)); 
} 
/** 
* 创建角色,返回角色记录主键 
* 
* @param string $rolename 
* @param string $desc 
* 
* @return int 
*/ 
function createRole($rolename,$desc){ 
if (empty($rolename)) return false; 
$role = array( 
'rolename' => $rolename, 
'desc' => $desc, 
'created_at' => CURRENT_TIMESTAMP 
); 
return SingleTableCRUD::insert($this->tbRoles,$role); 
} 
/** 
* 修改角色,返回成功状态 
* 
* @param array $role 
* @return int 
*/ 
function updateRole(array $role){ 
if (!isset($role['id'])) return false; 
if (isset($role['rolename'])) unset($role['rolename']); 
$role['updated_at'] = CURRENT_TIMESTAMP; 
return SingleTableCRUD::update($this->tbRoles,$role,'id'); 
} 
/** 
* 删除角色 
* 
* @param int $role_id 
* @return int 
*/ 
function deleteRole($role_id){ 
if (empty($role_id)) return false; 
return SingleTableCRUD::delete($this->tbRoles,array('role_id'=>(int) $role_id)); 
} 
/** 
* 为资源指定角色,每次均先全部移除表中相关记录再插入 
* 
* @param int $rsid 
* @param mixed $roleIds 
* @param boolean $setNull 当角色id不存在时,是否将资源从关联表中清空 
*/ 
function allocateRolesForResource($rsid,$roleIds,$setNull=false,$defaultAccess=-1){ 
if (empty($rsid)) return false; 
$roleIds = normalize($roleIds,','); 
if (empty($roleIds)){ 
if ($setNull){ 
SingleTableCRUD::delete($this->tbRefResourcesRoles,array('rsid'=>$rsid)); 
if ($defaultAccess != -1){ 
$defaultAccess = self::formatAccessValue($defaultAccess); 
$this->updateResource(array('rsid'=>$rsid,'access'=>$defaultAccess)); 
} 
return true; 
} 
return false; 
} 
SingleTableCRUD::delete($this->tbRefResourcesRoles,array('rsid'=>$rsid)); 
$roleIds = array_unique($roleIds); 
foreach ($roleIds as $role_id){ 
SingleTableCRUD::insert($this->tbRefResourcesRoles,array('rsid'=>$rsid,'role_id'=>(int)$role_id)); 
} 
return true; 
} 
function cleanRolesForResource($rsid){ 
if (empty($rsid)) return false; 
return SingleTableCRUD::delete($this->tbRefResourcesRoles,array('rsid'=>$rsid)); 
} 
function cleanResourcesForRole($role_id){ 
if (empty($role_id)) return false; 
return SingleTableCRUD::delete($this->tbRefResourcesRoles,array('role_id'=>(int) $role_id)); 
} 
/** 
* 为角色分配资源,每次均先全部移除表中相关记录再插入 
* 
* @param int $role_id 
* @param mixed $rsids 
* 
* @return boolean 
*/ 
function allocateResourcesForRole($role_id,$rsids){ 
if (empty($role_id)) return false; 
$role_id = (int) $role_id; 
$rsids = normalize($rsids,','); 
if (empty($rsids)){ 
return false; 
} 
SingleTableCRUD::delete($this->tbRefResourcesRoles,array('role_id'=>$role_id)); 
$rsids = array_unique($rsids); 
foreach ($rsids as $rsid){ 
SingleTableCRUD::insert($this->tbRefResourcesRoles,array('rsid'=>$rsid,'role_id'=>$role_id)); 
} 
return true; 
} 
/** 
* 为用户指派角色,每次均先全部移除表中相关记录再插入 
* 
* 此处在用户很多的时候可能会有性能问题 ... 后面再想怎么优化 
* 
* @param int $user_id 
* @param mixed $roleIds 
* 
* @return boolean 
*/ 
function allocateRolesForUser($user_id,$roleIds){ 
if (empty($user_id)) return false; 
$user_id = (int) $user_id; 
$roleIds = normalize($roleIds,','); 
if (empty($roleIds)){ 
return false; 
} 
SingleTableCRUD::delete($this->tbRefUsersRoles,array('user_id'=>$user_id)); 
$roleIds = array_unique($roleIds); 
foreach ($roleIds as $roleId){ 
SingleTableCRUD::insert($this->tbRefUsersRoles,array('user_id'=>$user_id,'role_id'=>$role_id)); 
} 
return true; 
} 
/** 
* 清除用户的角色信息 
* 
* @param int $user_id 
* 
* @return boolean 
*/ 
function cleanRolesForUser($user_id){ 
if (empty($user_id)) return false; 
return SingleTableCRUD::delete($this->tbRefUsersRoles,array('user_id'=>(int) $user_id)); 
} 
/** 
* 清除角色的用户关联 
* 
* @param int $role_id 
* 
* @return boolean 
*/ 
function cleanUsersForRole($role_id){ 
if (empty($role_id)) return false; 
return SingleTableCRUD::delete($this->tbRefUsersRoles,array('role_id'=>(int) $role_id)); 
} 
}

具体 检测的代码 如下:
/** 
* 对资源进行acl校验 
* 
* @param string $rsid 资源标识 
* @param array $user 特定用户,不指定则校验当前用户 
* 
* @return boolean 
*/ 
function aclVerity($rsid ,array $user = null){ if (empty($rsid)) return false; 
if (!CoreApp::$defaultAcl) { 
CoreApp::$defaultAcl = new AclFlat(); 
} 
$rsRow = aclGetResource($rsid); 
// 未定义资源的缺省访问策略 
if (!$rsRow) return false; 
CoreApp::writeLog($rsRow,'test'); 
$rsRow['access'] = AclBase::formatAccessValue($rsRow['access']); 
// 允许任何人访问 
if (AclBase::EVERYONE == $rsRow['access']) return true; 
// 不允许任何人访问 
if (AclBase::NOBODY == $rsRow['access']) return false; 
// 获取用户信息 
if (empty($user)) $user = isset($_SESSION['SI-SysUser']) ? $_SESSION['SI-SysUser'] : null; 
// 用户未登录,则当成无访问权限 
if (empty($user)) return false; 
$user['roles'] = empty($user['roles']) ? null : normalize($user['roles'],';'); 
$userHasRoles = !empty($user['roles']); 
/** 
* 允许 不带有角色的用户访问 
*/ 
if (AclBase::NO_ROLE == $rsRow['access']) return $userHasRoles ? false : true; 
/** 
* 允许 带有角色的用户访问 
*/ 
if (AclBase::HAS_ROLE == $rsRow['access']) return $userHasRoles ? true : false; 
// --- 对用户进行 资源 <-> 角色 校验 
if ($userHasRoles){ 
foreach ($user['roles'] as $role_id){ 
if ( aclGetRefResourcesRoles($rsid,$role_id) ) 
return true; 
} 
dump($user); 
} 
return false; 
}

/** 
* 对资源进行acl校验 
* 
* @param string $rsid 资源标识 
* @param array $user 特定用户,不指定则校验当前用户 
* 
* @return boolean 
*/ 
function aclVerity($rsid ,array $user = null){ if (empty($rsid)) return false; 
if (!CoreApp::$defaultAcl) { 
CoreApp::$defaultAcl = new AclFlat(); 
} 
$rsRow = aclGetResource($rsid); 
// 未定义资源的缺省访问策略 
if (!$rsRow) return false; 
CoreApp::writeLog($rsRow,'test'); 
/* 
* 校验步骤如下: 
* 
* 1. 先校验 资源本身 access 属性 
* EVERYONE => true,NOBODY => false * 其它的属性在下面继续校验 
* 2. 从 session(或者 用户session表)中获取角色id集合 
* 3. 如果 用户拥有角色 则 HAS_ROLE => true , NO_ROLE => false;反之亦然 
* 4. 如果资源 access == ALLOCATE_ROLES 
* 1. 从缓存(或者 $tbRefResourcesRoles)中获取 资源对应的角色id集合 
* 2. 将用户拥有的角色id集合 与 资源对应的角色id集合求交集 
* 3. 存在交集 => true;否则 => false 
*/ 
$rsRow['access'] = AclBase::formatAccessValue($rsRow['access']); 
// 允许任何人访问 
if (AclBase::EVERYONE == $rsRow['access']) return true; 
// 不允许任何人访问 
if (AclBase::NOBODY == $rsRow['access']) return false; 
// 获取用户信息 
if (empty($user)) $user = isset($_SESSION['SI-SysUser']) ? $_SESSION['SI-SysUser'] : null; 
// 用户未登录,则当成无访问权限 
if (empty($user)) return false; 
$user['roles'] = empty($user['roles']) ? null : normalize($user['roles'],';'); 
$userHasRoles = !empty($user['roles']); 
/** 
* 允许 不带有角色的用户访问 
*/ 
if (AclBase::NO_ROLE == $rsRow['access']) return $userHasRoles ? false : true; 
/** 
* 允许 带有角色的用户访问 
*/ 
if (AclBase::HAS_ROLE == $rsRow['access']) return $userHasRoles ? true : false; 
// --- 对用户进行 资源 <-> 角色 校验 
if ($userHasRoles){ 
foreach ($user['roles'] as $role_id){ 
if ( aclGetRefResourcesRoles($rsid,$role_id) ) 
return true; 
} 
dump($user); 
} 
return false; 
} 
/** 
* 重新生成 角色资源访问控制表 
* 
* @param string $actTable ACL表名称 
* @param boolean $return 是否返回重新生成的列表 
* 
* @return mixed 
*/ 
function aclRebuildACT($actTable ,$return = false){ 
if (empty($actTable)) return false; 
global $globalConf; 
$rst = null; 
$cacheId = null; 
switch($actTable){ 
case CoreApp::$defaultAcl->tbResources: 
$cacheId = 'acl-resources'; 
$rst = SingleTableCRUD::findAll(CoreApp::$defaultAcl->tbResources); 
// 转成 哈希表结构 
if ($rst){ 
$rst = array_to_hashmap($rst,'rsid'); 
} 
break; 
case CoreApp::$defaultAcl->tbRoles: 
$cacheId = 'acl-roles'; 
$rst = SingleTableCRUD::findAll(CoreApp::$defaultAcl->tbRoles); 
// 转成 哈希表结构 
if ($rst){ 
$rst = array_to_hashmap($rst,'id'); 
} 
break; 
case CoreApp::$defaultAcl->tbRefResourcesRoles: 
$cacheId = 'acl-roles_has_resources'; 
$rst = SingleTableCRUD::findAll(CoreApp::$defaultAcl->tbRefResourcesRoles); 
if ($rst){ 
$_ = array(); 
foreach ($rst as $row) { 
$ref_id = "{$row['rsid']}<-|->{$row['role_id']}"; 
$_[$ref_id] = $row; 
} 
unset($rst); 
$rst = $_; 
} 
break; 
} 
if ($cacheId) 
writeCache($globalConf['runtime']['cacheDir'] ,$cacheId ,$rst ,true); 
if ($return) return $rst; 
} 
/** 
* 获取 角色资源访问控制表 数据 
* 
* @param string $actTable ACL表名称 
* 
* @return mixed 
*/ 
function aclGetACT($actTable){ 
if (empty($actTable)) return false; 
static $rst = array(); 
$cacheId = null; 
switch($actTable){ 
case CoreApp::$defaultAcl->tbResources: 
$cacheId = 'acl-resources'; 
break; 
case CoreApp::$defaultAcl->tbRoles: 
$cacheId = 'acl-roles'; 
break; 
case CoreApp::$defaultAcl->tbRefResourcesRoles: 
$cacheId = 'acl-roles_has_resources'; 
break; 
} 
if (!$cacheId) return null; 
if (isset($rst[$cacheId])) return $rst[$cacheId]; 
global $globalConf; 
// 900 
$rst[$cacheId] = getCache($globalConf['runtime']['cacheDir'],$cacheId,0); 
if ( !$rst[$cacheId] ){ 
$rst[$cacheId] = aclRebuildACT($actTable,true); 
} 
return $rst[$cacheId]; 
} 
/** 
* 获取 资源 记录 
* 
* @param string $rsid 
* 
* @return array 
*/ 
function aclGetResource($rsid){ 
static $rst = null; 
if (!$rst){ 
$rst = aclGetACT(CoreApp::$defaultAcl->tbResources); 
if (!$rst) $rst = array(); 
} 
return isset($rst[$rsid]) ? $rst[$rsid] : null; 
} 
/** 
* 获取 角色 记录 
* 
* @param int $role_id 
* 
* @return array 
*/ 
function aclGetRole($role_id){ 
static $rst = null; 
if (!$rst){ 
$rst = aclGetACT(CoreApp::$defaultAcl->tbRoles); 
if (!$rst) $rst = array(); 
} 
return isset($rst[$role_id]) ? $rst[$role_id] : null; 
} 
/** 
* 获取 用户角色关联 记录,此方法可以校验资源是否可被此角色调用 
* 
* @param string $rsid 
* @param int $role_id 
* 
* @return array 
*/ 
function aclGetRefResourcesRoles($rsid,$role_id){ 
static $rst = null; 
if (!$rst){ 
$rst = aclGetACT(CoreApp::$defaultAcl->tbRefResourcesRoles); 
if (!$rst) $rst = array(); 
} 
$ref_id = "{$rsid}<-|->{$role_id}"; 
CoreApp::writeLog(isset($rst[$ref_id])?$rst[$ref_id]:'nodata',$ref_id); 
return isset($rst[$ref_id]) ? $rst[$ref_id] : null; 
}

http://code.google.com/p/php-excel/downloads/list 迷你好用的 EXCEL xml 输出方案
PHP 相关文章推荐
使用PHP数组实现无限分类,不使用数据库,不使用递归.
Dec 09 PHP
js下函数般调用正则的方法附代码
Jun 22 PHP
PHP编码转换
Nov 05 PHP
C/S和B/S两种架构区别与优缺点分析
Oct 23 PHP
PHP中file_exists()判断中文文件名无效的解决方法
Nov 12 PHP
php将字符串随机分割成不同长度数组的方法
Jun 01 PHP
smarty的section嵌套循环用法示例
May 28 PHP
PHP+JQuery+Ajax实现分页方法详解
Aug 06 PHP
PHP 进程池与轮询调度算法实现多任务的示例代码
Nov 26 PHP
PHP程序员简单的开展服务治理架构操作详解(一)
May 14 PHP
PHP copy函数使用案例代码解析
Sep 01 PHP
详解Laravel服务容器的优势
May 29 PHP
php将时间差转换为字符串提示
Sep 07 #PHP
php 中英文语言转换类
Sep 07 #PHP
php继承的一个应用
Sep 06 #PHP
php 抽象类的简单应用
Sep 06 #PHP
PHP中PDO基础教程 入门级
Sep 04 #PHP
PHP中PDO的错误处理
Sep 04 #PHP
php中将网址转换为超链接的函数
Sep 02 #PHP
You might like
使用php重新实现PHP脚本引擎内置函数
2007/03/06 PHP
PHP开发中常用的三个表单验证函数使用小结
2010/03/03 PHP
php提供实现反射的方法和实例代码
2019/09/17 PHP
JavaScript 实现??打印?理
2007/04/28 Javascript
jquery 图片 上一张 下一张 链接效果(续篇)
2010/04/20 Javascript
js getBoundingClientRect() 来获取页面元素的位置
2010/11/25 Javascript
学习js在线html(富文本,所见即所得)编辑器
2012/12/18 Javascript
用js实现小球的自由移动代码
2013/04/22 Javascript
js中生成map对象的方法
2014/01/09 Javascript
jquery append()方法与html()方法的区别及使用介绍
2014/08/01 Javascript
JavaScript编程学习技巧汇总
2016/02/21 Javascript
JavaScript中动态向表格添加数据
2017/01/24 Javascript
nodejs搭建本地服务器并访问文件的方法
2017/03/03 NodeJs
JavaScript实现自动跳转文本功能
2017/05/25 Javascript
jfinal与bootstrap的登出实战详解
2017/11/27 Javascript
javascript显示动态时间的方法汇总
2018/07/06 Javascript
vue如何截取字符串
2019/05/06 Javascript
微信小程序关键字变色实现代码实例
2019/12/13 Javascript
[52:08]DOTA2上海特级锦标赛主赛事日 - 3 败者组第三轮#2Fnatic VS OG第一局
2016/03/05 DOTA
python 正则式使用心得
2009/05/07 Python
python打开网页和暂停实例
2014/09/30 Python
使用python实现tcp自动重连
2017/07/02 Python
Python 数据处理库 pandas 入门教程基本操作
2018/04/19 Python
python单例模式获取IP代理的方法详解
2018/09/13 Python
Python实现bilibili时间长度查询的示例代码
2020/01/14 Python
英国最大的纸工艺品商店:CraftStash
2018/12/01 全球购物
如何利用find命令查找文件
2016/11/18 面试题
资产经营总监岗位职责范文
2013/12/01 职场文书
关于安全的演讲稿
2014/05/09 职场文书
副总经理任命书
2014/06/05 职场文书
在教室放鞭炮的检讨书
2014/09/28 职场文书
2014年卫生院工作总结
2014/12/03 职场文书
电影雷锋观后感
2015/06/10 职场文书
消夏晚会主持词
2015/06/30 职场文书
Python selenium绕过webdriver监测执行javascript
2022/04/12 Python
Linux中如何安装并部署Redis
2022/04/18 Servers