php中实现简单的ACL 完结篇

复制代码 代码如下:

  -- 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 输出方案