<<采用mixin原理实现的rbac解决方案>>
作者:axgle
功能特征(write less,do more):
1.支持一个用户扮演多个角色
2.角色可以继承,可以包含其他角色
3.支持task,task就像角色一样
4.支持business rule(业务规则)
5.支持统一的operation名称检查(例如master的edit和author的edit,edit名称相同,但业务规则不同,author只能编辑自己的,但权限检查的时候依然用"edit"这个名称).
实现原理:
用户与AuthItem之间的关系约定:
1.AuthItem的定义: 角色,task,operation,统称AuthItem
2.mixin原则: 任何AuthItem可以有多个Child与之关联(把Child AuthItem mixin到某个AuthItem)
3.单项分配原则: 一个用户只能有一个"直接"的AuthItem与之关联
根据以上约定,则自然一个用户可以有多个AuthItem与之"间接"关联(可以解决一个用户多个角色的问题).
1197
检查用户是否具有某个操作权限,就是看该用户所属的AuthItem的层次关系是否连通.
形象的说,用户关联到一颗树的根节点,该根节点就是用户所属的AuthItem($name).首先检查根节点($name)的直接child中是否有待检查的目标($child),有就成功返回.没有就查找目标($child)的父节点,把该父节点当成新的目标,递归检查即可.如下采用Yii的数据库操作方法的演示:
function checkAccess($name,$child,$params=array()){
$criteria=new CDbCriteria();
$criteria->compare('name',$name);
$criteria->compare('child',$child);
$model=AuthChild::model()->find($criteria);
//var_dump($model);
if($model){
if($model->rule){
return eval($model->rule);
}
return true;
}else{
$criteria=new CDbCriteria();
$criteria->compare('child',$child);
$models=AuthChild::model()->findAll($criteria);
foreach($models as $model){
if(checkAccess($name,$model->name, $params)){ return true;}
}
}
return false;
}
echo (int)checkAccess('author', 'editAsset',3);
auth_schema.sql
SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";
CREATE TABLE IF NOT EXISTS `auth_child` (
`name` varchar(100) NOT NULL,
`child` varchar(100) NOT NULL,
`rule` text,
UNIQUE KEY `name_UNIQUE` (`name`,`child`),
KEY `fk_mixin_item` (`name`),
KEY `fk_mixin_item1` (`child`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `auth_child` (`name`, `child`, `rule`) VALUES
('author', 'edit', 'return $params==3;'),
('author', 'view', NULL),
('edit', 'editAsset', NULL),
('edit', 'editCampaign', NULL),
('master', 'edit', NULL),
('master', 'view', NULL),
('viewer', 'view', NULL);
CREATE TABLE IF NOT EXISTS `auth_item` (
`name` varchar(100) NOT NULL,
`desc` text,
PRIMARY KEY (`name`),
UNIQUE KEY `name_UNIQUE` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COMMENT='desc';
INSERT INTO `auth_item` (`name`, `desc`) VALUES
('author', NULL),
('edit', NULL),
('editAsset', NULL),
('editCampaign', NULL),
('master', NULL),
('view', NULL),
('viewer', NULL);
ALTER TABLE `auth_child`
ADD CONSTRAINT `fk_mixin_item` FOREIGN KEY (`name`) REFERENCES `auth_item` (`name`) ON DELETE CASCADE ON UPDATE CASCADE,
ADD CONSTRAINT `fk_mixin_item1` FOREIGN KEY (`child`) REFERENCES `auth_item` (`name`) ON DELETE CASCADE ON UPDATE CASCADE;