很多网站设置了多个角色并且每个角色都具备不同的权限。
基于角色的访问控制可以采用二进制串的形式或者字符串的形式来灵活处理。
二进制串的基本原理:
每种权限对应一个二进制值,二进制位只能为0或1,这样就很好的处理了权限的拥有与否。(0表示无此权限,1表示拥有此权限)。
我们需要一个权限表或者字符映射(宏定义)来定义不同的权限。
一般采用定义数据表的形式来存取权限,这样的话将方便权限的编辑、添加与删除。
定义表的代码:
# Dumping structure for table mcp.mcp_premission
DROP TABLE IF EXISTS `mcp_premission`;
CREATE TABLE IF NOT EXISTS `mcp_premission` (
`premission_id` int(1) NOT NULL COMMENT '权限ID',
`premission_name` char(20) NOT NULL COMMENT '权限名称',
`system_reserved` tinyint(1) NOT NULL DEFAULT '0' COMMENT '权限属性',
PRIMARY KEY (`premission_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
# Dumping data for table mcp.mcp_premission: 13 rows
DELETE FROM `mcp_premission`;
INSERT INTO `mcp_premission` (`premission_id`, `premission_name`, `system_reserved`) VALUES
(0, '编辑共享会议模板', 1),
(1, '查看共享会议模板', 1),
(2, '调度会议', 1),
(3, '会议控制', 1),
(4, '会议监控', 1),
(5, '编辑系统组织结构', 1),
(6, '编辑用户', 1),
(7, '编辑Mcu', 1),
(8, '编辑视频会议终端', 1),
(9, '编辑录像服务器', 1),
(10, '编辑权限组', 1),
(11, '系统维护', 1),
(12, '编辑会议模板', 1);
相对应表结构:
权限ID必须从0开始,表示二进制串的第一位(从右边起)。
设第二是定义权限组表,每个权限组对应一些权限,用户属于某个组就拥有某种权限。
定义表的代码
# Dumping structure for table mcp.mcp_premission_group
DROP TABLE IF EXISTS `mcp_premission_group`;
CREATE TABLE IF NOT EXISTS `mcp_premission_group` (
`premission_group_id` int(1) NOT NULL COMMENT '权限组ID',
`premission_group_name` char(20) NOT NULL COMMENT '权限组名称',
`premission_masks` int(1) NOT NULL DEFAULT '0' COMMENT '权限集',
`system_reserved` tinyint(1) NOT NULL DEFAULT '0' COMMENT '权限组属性',
PRIMARY KEY (`premission_group_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
# Dumping data for table mcp.mcp_premission_group: 2 rows
DELETE FROM `mcp_premission_group`;
INSERT INTO `mcp_premission_group` (`premission_group_id`, `premission_group_name`, `premission_masks`, `system_reserved`) VALUES
(0, '会议控制组', 0, 1),
(1, '资源管理组', 0, 1);
相对应表结构:
同理权限组ID也应该从0开始,权限集字段记录了所拥有权限。
还需要一个用户表,角色控制最关键的元素当然是我们需要来管理的用户。
定义表的代码
# Dumping structure for table mcp.mcp_user
DROP TABLE IF EXISTS `mcp_user`;
CREATE TABLE IF NOT EXISTS `mcp_user` (
`user_account` char(20) NOT NULL COMMENT '用户名',
`user_name` char(20) NOT NULL COMMENT '用户姓名',
`area` char(10) NOT NULL COMMENT '用户所属区域',
`user_password` char(32) NOT NULL COMMENT '用户密码',
`user_tel` char(20) DEFAULT NULL COMMENT '联系电话',
`user_email` char(20) DEFAULT NULL COMMENT '联系邮箱',
`premission_group_masks` int(1) NOT NULL DEFAULT '0' COMMENT '权限组集',
`user_online_status` char(4) NOT NULL DEFAULT '离线' COMMENT '用户状态',
PRIMARY KEY (`user_account`),
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
最关键的字段在于权限组集字段,该字段定义了用户所属的权限组。
由于需要管理的权限不多(在我的情况下不会超过32个),所以权限ID、权限组ID、权限组表的权限集和用户表的权限组集都采取了int(无符号)形式存储,在大多数机器上那个整型为四个字节即32位。这样我们就可以利用一个整型的ID来表示32个不同的权限和权限组。32对应的权限组表的权限集和用户表的权限组集的最大值(即32为二进制位全部为1的情况)是2^32 - 1 = 65535。
00000000 00000000 00000000 00000000
从我们上面的权限表中我们可以看到编辑共享会议模板 和 查看会议共享模板对应的权限ID分别是0和1。代表的意思是上面32位二进制串从右边起的第一位和第二位。当这两个二进制位全为1时对应的权限值为2^0 = 1 和 2^1 = 2 对应的二进制位移操作为 1 << 0 = 1 和 1 << 1 =2
同理权限组表中的 资源管理组和会议控制组定义和上面一样。
我们现在可以看到会议控制组的权限集字段为0,代表该组目前尚无任何权限。
我们可以开始定义权限组的权限。假设资源管理组的拥有的权限为(编辑共享会议模板和查看会议模板),此时(权限集字段的32位二进制串从右起第一位和第二位对应为1)则对应的权限集字段应该为2^0 + 2^1 = 3,所以存储在数据库中的权限集为3.对应的二进制位移操作为 (1 << 0) | (1 << 1) =3
再定义用户所拥有的权限,用户权限通过用户组来来体现。假设某用户拥有资源管理组和会议控制组的所有权限,(根据上面我们定义了资源管理组对应的权限而未定义会议控制组的权限)则用户表权限组集字段的值为 2^0 + 2^1 = 3(权限组集字段的32位二进制串从右起第一位和第二位对应为1)。
这样即可轻松实现角色权限管理和控制。
体现的关系应该为:用户 -> 权限组 -> 权限,用户与权限组、权限组和权限都是多对多的关系。
角色权限的访问,我们可以通过二进制的位移操作来解析用户权限组集对应的权限组,进而通过权限组的权限集字段解析对应的权限。所有是十进制的数在计算机中都是采用二进制形式存储,所以我们可以轻松的将权限组集和权限集的十进制换成对应的二进制数值之和。
定义以下函数:(分解十进制的值)
@count 为数据库中对应权限表和权限组表分别对应的权限和权限组的个数。
@value 为我们要分解的权限组集值或者权限集值
function calculate_values($value, $count){for ($i = 0; $i < $count; $i ++){
if ($bit = ($value & 0x01))//与1与,判断$value > 0
{
$stack[] = $i;//入栈最小的2次方幂的幂
}
$value >>= 1;//右移一位
}
return $stack;
}
我们数据库中权限组表拥有的权限组个数为2,则我们资源管理组对应的权限集为3(上面已经定义)
则 calculate_values(3, 2)的值最终为数组 stack(0,1);通过0和1我们可以在权限表里查询0和1的ID多对应的权限。即3 = (1 << 0) | (1 << 1)
用户所具备的权限最终是通过权限组来实现的,我们添加权限组的权限同样是通过位移操作来实现。上面我们已经实现。我的权限组实现添加权限是通过复选来实现的
function modify_premission_group()
{
document.getElementById("result").style.color = "red";
if (document.getElementById("premission_group_id"))
{
var premission_group_id = document.getElementById("premission_group_id").value;
}
else
{
var premission_group_id = "";
}
var premission_group_name = document.getElementById('premission_group_name').value;
if (premission_group_name == "")
{
document.getElementById("result").innerHTML="权限组名称不能为空";
return;
}
//获取已经勾选的权限值
var datas = new Array();
var chops = document.getElementsByName('chops[]');
for (i = 0; i < chops.length ; i ++ )
{
if(chops[i].checked == true)
{
datas.push(chops[i].value);
}
}
if(datas.length == 0)
{
document.getElementById("result").innerHTML="请选择权限";
return;
}
var formdata = "premission_group_name="+premission_group_name+"&datas="+datas+"&premission_group_id="+premission_group_id;
ajax("modify_premission_group_data.php",formdata,function()
{
if (xmlReq.readyState == 4 && xmlReq.status == 200)
{
document.getElementById("result").innerHTML = xmlReq.responseText;
delay_result();
}
});
}
复选的框提交后传递过来的是权限的ID,通过ajax组合传过来ID的数组,然后在后台分解ID。
<span style="white-space:pre"> </span>$datas<span style="white-space:pre"> </span>= $_POST['datas'];
<span style="white-space:pre"> </span>//得到权限名称,数组形式
<span style="white-space:pre"> </span>$premission_name_array = explode(",", $datas);
<span style="white-space:pre"> </span>$premission_masks = 0;
<span style="white-space:pre"> </span>for($i = 0; $i < count($premission_name_array); $i++ )
{
$sql = "select * from mcp_premission where `premission_name` ='{$premission_name_array[$i]}' ";
$query = $db ->query($sql);
$premission_id = $db ->get_field($query, '0', 'premission_id');
$premission_id = 1 << $premission_id;
$premission_masks |= $premission_id;
}
将1左移对应的ID值位则得到对应的权限值,然后将权限值与权限集值相或得到新的权限值。
到此基于角色的访问控制已经完成。
简述:
三张表:用户表,权限组表,权限组
添加权限 相或 |
删除权限 和改权限都可以重新组合权限 即都用相或 |
判断是否具备某权限与该权限的权限值相与 &
最后的就是权限集值和权限组集值的分解了。