权限设计文档
权限设计通常包括数据库设计、应用程序接口
(API)
设计、程序实现三个部分。
这三个部分相互依存,密不可分,要实现完善的权限管理体系,必须考虑到每一个环节可行性与复杂程度甚至执行效率。
权限分类:
1、
功能(服务级别):它可以包括例如统计等所有非直接数据存取操作
2、
数据表存取的权限(表级别):通常有录入、浏览、修改、删除四种,
3、
字段的存取的权限(字段级别):对一些关键数据表某些字段的存取进行限制。
完善的权限设计应该具有充分的可扩展性,也就是说,系统增加了新的其它功能不应该对整个权限管理体系带来较大的变化,要达到这个目的,首先是数据库设计合理,其次是应用程序接口规范。
权限表及相关内容大体可以用六个表来描述,如下:
1 角色(即用户组)表:包括三个字段, ID ,角色名,对该角色的描述;
2 用户表:包括三个或以上字段, ID ,用户名,对该用户的描述,其它(如地址、电话等信息);
3 角色 - 用户对应表:该表记录用户与角色之间的对应关系,一个用户可以隶属于多个角色,一个角色组也可拥有多个用户。包括三个字段, ID ,角色 ID ,用户 ID ;
4 限制内容列表:该表记录所有需要加以权限区分限制的数据表、功能和字段等内容及其描述,包括三个字段, ID ,名称,描述;
5 权限列表:该表记录所有要加以控制的权限,如录入、修改、删除、执行等,也包括三个字段, ID ,名称,描述;
6 权限 - 角色 - 用户对应表:一般情况下,我们对角色 / 用户所拥有的权限做如下规定,角色拥有明令允许的权限,其它一律禁止,用户继承所属角色的全部权限,在此范围内的权限除明令禁止外全部允许,范围外权限除明令允许外全部禁止。
1 角色(即用户组)表:包括三个字段, ID ,角色名,对该角色的描述;
2 用户表:包括三个或以上字段, ID ,用户名,对该用户的描述,其它(如地址、电话等信息);
3 角色 - 用户对应表:该表记录用户与角色之间的对应关系,一个用户可以隶属于多个角色,一个角色组也可拥有多个用户。包括三个字段, ID ,角色 ID ,用户 ID ;
4 限制内容列表:该表记录所有需要加以权限区分限制的数据表、功能和字段等内容及其描述,包括三个字段, ID ,名称,描述;
5 权限列表:该表记录所有要加以控制的权限,如录入、修改、删除、执行等,也包括三个字段, ID ,名称,描述;
6 权限 - 角色 - 用户对应表:一般情况下,我们对角色 / 用户所拥有的权限做如下规定,角色拥有明令允许的权限,其它一律禁止,用户继承所属角色的全部权限,在此范围内的权限除明令禁止外全部允许,范围外权限除明令允许外全部禁止。
其中第六条是至关重要的部分,主要解决方案有以下几种:
(1)
设计五个字段:
ID
,限制内容
ID
,权限
ID
,角色
/
用户类型(布尔型字段,用来描述一条记录记录的是角色权限还是用户权限),角色
/
用户
ID
,权限类型(布尔型字段,用来描述一条记录表示允许还是禁止)
优点:
可以对角色和用户分别进行权限定制,也具有相当的可扩展性,比如说增加了新功能,我们只需要添加一条或者几条记录就可以,同时应用程序接口也无须改动,具有相当的可行性
缺点:使用这种方法并不是十分科学,例如浏览某个用户所拥有的权限时,需要对数据库进行多次(甚至是递归)查询,极不方便。
解决方案:
(
假定录入操作的权限
ID
为
1
,浏览操作的权限
ID
为
2
,修改操作的权限
ID
为
3
,删除操作的权限
ID
为
4
同时假定修改操作包括了浏览操作权限,删除操作包括了修改操作的权限
在表 5 (权限列表)中加入一个字段:权限值
那么可以设定:录入操作的权限值为 1 ,浏览操作的权限值为 2 ,修改操作的权限值为 32 ,删除操作的权限值为 432 ,这样就解决了权限包容关系
同时我们规定,各权限描述数据在表 6 (权限 - 角色 - 用户对应表)中存储的数据为权限值的简单累加
这样,当我们读到某用户对库存表的权限为 1432 (即拥有录入、删除权限)时,如果要判断他是否具有浏览权限,只要简单的查找到该字符串中是否有 2 便可,这种情况下,权限代码 1432 和 12432 , 1232432 表示的意思是一样的,读取的方式也相同。
同时假定修改操作包括了浏览操作权限,删除操作包括了修改操作的权限
在表 5 (权限列表)中加入一个字段:权限值
那么可以设定:录入操作的权限值为 1 ,浏览操作的权限值为 2 ,修改操作的权限值为 32 ,删除操作的权限值为 432 ,这样就解决了权限包容关系
同时我们规定,各权限描述数据在表 6 (权限 - 角色 - 用户对应表)中存储的数据为权限值的简单累加
这样,当我们读到某用户对库存表的权限为 1432 (即拥有录入、删除权限)时,如果要判断他是否具有浏览权限,只要简单的查找到该字符串中是否有 2 便可,这种情况下,权限代码 1432 和 12432 , 1232432 表示的意思是一样的,读取的方式也相同。
)
分析过程:
使用过
Unix
系统的人们都知道,
Unix
文件系统将对文件的操作权限分为三种:读、写和执行,分别用
1
、
2
、
4
三个代码标识,对用户同时具有读写权限的文件被记录为
3
,即
1+2
。我们也可以用类似的办法来解决这个问题。初步的想法是修改权限列表,加入一个字段:标识码,例如,我们可以将录入权限标识为
1
,浏览权限标识为
2
,修改权限标识为
4
,删除权限标识为
8
,执行权限标识为
16
,这样,我们通过权限累加的办法就可以轻易的将原本要分为几条记录描述的权限放在一起了,例如,假定某用户
ID
为
1
,库存表对应的限制内容
ID
为
2
,同时规定角色类型为
0
、用户类型为
1
,我们就可以将该用户具有录入、浏览、修改、删除库存表的权限描述为:
2,15,1,1
。
我们所涉及的权限列表不是相互独立而是互相依赖的,比如说修改权限,其实是包含浏览权限的,例如,我们可能只是简单的设置用户对库存表存取的权限值为录入
+
修改
+
删除(
1+4+8=13),
但事实上,该用户具有
(1+2+4+8=15
)的权限,也就是说,在这种方案中,
13=15
。于是当我们调用
API
询问某用户是否具有浏览权限时,就必须判断该用户是否具有对该数据表的修改权限,因此,如果不能在程序中固化权限之间的包含关系,就不能利用应用程序接口简单的做出判断。但这与我们的目的
“
充分的可扩展性
”
矛盾。
另外一种设置标识码的方法,那就是利用素数。我们不妨将录入、浏览、修改、删除、执行的基本标志码定为
2,3,5,7,11
,当遇到权限互相包含的时候,我们将它的标识码设定为两个(或多个)基本标志码的乘积,例如,可以将
“
修改
”
功能的标志码定为
3*5=15
,然后将所有的权限相乘,就得到了我们需要的最终权限标识值。这样,我们在询问用户是否具有某项权限的时候,只需要将最终的值分解成质因子,例如,我们可以定义一个用户具有录入
+
修改
+
删除库存表的权限为
2*15*7=2*3*5*7
,即表示,该用户具有了对库存表录入
+
浏览
+
修改
+
删除权限。
(数字组成的字符串)
//
{
全部由数字组成,每两位数字表示一组权限时,则每组最多能包容
7
种权限
(2^8=64)
,以及
99-63=36
个可扩展数字,(每组
3
位数字时有
8
种权限,以此类推)。那么所谓的权限包容,比如说权限值为
0100
表示可读,
0200
表示可写,操作人员权限值为
0600,
那么因为
1&6=0
,所以该人员没有读的权限,但是
2&6=2
,故该人员有写的权限
,
如果是
0300
,则即可读也写
至于所谓的角色,说到底其实还是权限的问题,一个角色其实可以具体表示为一个权限值,多个角色之间的所包含的权限或许有重复,但不同角色本身的权限值是不会相同的,具有多种角色的人员,它的权限其实就是每个角色的权限值相或所得的结果。
至于所谓的角色,说到底其实还是权限的问题,一个角色其实可以具体表示为一个权限值,多个角色之间的所包含的权限或许有重复,但不同角色本身的权限值是不会相同的,具有多种角色的人员,它的权限其实就是每个角色的权限值相或所得的结果。
}
/
当然,对权限列表我们使用上述方法的前提是权限列表记录条数不会太多并且关系不是十分复杂
(2)
1
、用以实现对界面对象(如菜单、按钮)的权限控制。简称对象级权限
2 、用以实现对数据能否被某一个用户修改、删除的权限控制。建成数据级权限
2 、用以实现对数据能否被某一个用户修改、删除的权限控制。建成数据级权限
权限控制主要分为两个方面:
1
。你是否能够看到这个菜单功能;
2
。你是否能够处理这个菜单设计的事务功能(相对比较复杂,有按照微软的授权方式:完全控制,读,写,删除,管理
……
),现在大部分系统都是用菜单来控制权限的(绝大部分时候是够用了),但是我们还增加了一个基于流程事务功能上的授权。
主要有:人员表,角色表,部门表 , 级别表 菜单表
人员角色关系表 部门角色关系表 人员部门关系表
人员菜单表, 角色菜单表,
另外还有一套工作流程授权机制表,基本上能够控制得非常理想!
主要有:人员表,角色表,部门表 , 级别表 菜单表
人员角色关系表 部门角色关系表 人员部门关系表
人员菜单表, 角色菜单表,
另外还有一套工作流程授权机制表,基本上能够控制得非常理想!
如果用户
“Popeye”
对
“
销售出仓单
[2009]”
系统对象具有读写(读取+修改+删除+新增)权限:(权限表定义如下
TPermission
)
FormID UID Permission
======= ==== ==========
2009 Popeye 1+2+4+8=15
***** 上面系统定义的默认权限肯定是不够系统使用的,那么还有一些权限(例如:报关系统中的 “ 计算差异表 ”“ 制造申报单 ” 等权限,就由系统再定义),其实不用太担心会不够用的,因为在一个 Form 中不可能会出现所有权限情况,所以,系统自定义的权限掩码可重复使用在不同的表单中。 *****
建议不要把角色和用户分开两张表来存储(可参考 MS-SQL Server 中的 sys_users 表),因为在后面的权限定义表需要引用这个表的 UID (其可为用户或角色, SQL 中是使用 UID 的数值范围来区别用户与角色的,建议也如此。),版主说的角色与用户分开对待权限设置,这点我不赞成。因为角色只不过是一种用户组,其应该享用用户的权限定义范围,在其下属的角色成员(注意角色成员不同于用户或角色哦,其可以为角色也可以为用户)均默认继承其定义的权限,除非角色成员重新指派其上级角色定义的权限字。下面给出我的相关表定义:
TUser( 用户或角色表 )
===================
(PK)UID int NOT NULL (主键)
Name nvarchar(50) NOT NULL (唯一性约束)
FullName nvarchar(100) NULL
Description nvarchar(255) NULL
MasterNo varchar(25) NULL (注:该字段对应为员工表中的员工编号,通过该字段就可以关联知道该用户或角色所属的员工了,在企业管理系统中很有用啊!)
TMember( 用户与角色关系表 )
=========================
(*PK)RoleID int NOT NULL
(*PK)UserID int NOT NULL
TPermission( 用户权限表 )
=======================
(*PK)FormID int NOT NULL (表示系统中各个模块或表单的唯一编号)
(*PK)UserID int NOT NULL (用户或角色编号)
Protect bit NOT NULL ( 1 :表示显示授予权限; 0 :表示显示拒绝权限)
Permission int NOT NULL (权限掩码和)
FormID UID Permission
======= ==== ==========
2009 Popeye 1+2+4+8=15
***** 上面系统定义的默认权限肯定是不够系统使用的,那么还有一些权限(例如:报关系统中的 “ 计算差异表 ”“ 制造申报单 ” 等权限,就由系统再定义),其实不用太担心会不够用的,因为在一个 Form 中不可能会出现所有权限情况,所以,系统自定义的权限掩码可重复使用在不同的表单中。 *****
建议不要把角色和用户分开两张表来存储(可参考 MS-SQL Server 中的 sys_users 表),因为在后面的权限定义表需要引用这个表的 UID (其可为用户或角色, SQL 中是使用 UID 的数值范围来区别用户与角色的,建议也如此。),版主说的角色与用户分开对待权限设置,这点我不赞成。因为角色只不过是一种用户组,其应该享用用户的权限定义范围,在其下属的角色成员(注意角色成员不同于用户或角色哦,其可以为角色也可以为用户)均默认继承其定义的权限,除非角色成员重新指派其上级角色定义的权限字。下面给出我的相关表定义:
TUser( 用户或角色表 )
===================
(PK)UID int NOT NULL (主键)
Name nvarchar(50) NOT NULL (唯一性约束)
FullName nvarchar(100) NULL
Description nvarchar(255) NULL
MasterNo varchar(25) NULL (注:该字段对应为员工表中的员工编号,通过该字段就可以关联知道该用户或角色所属的员工了,在企业管理系统中很有用啊!)
TMember( 用户与角色关系表 )
=========================
(*PK)RoleID int NOT NULL
(*PK)UserID int NOT NULL
TPermission( 用户权限表 )
=======================
(*PK)FormID int NOT NULL (表示系统中各个模块或表单的唯一编号)
(*PK)UserID int NOT NULL (用户或角色编号)
Protect bit NOT NULL ( 1 :表示显示授予权限; 0 :表示显示拒绝权限)
Permission int NOT NULL (权限掩码和)
create table t_workelement /*
工作元素表
*/
(
code varchar(20) not null, /* 元素的代码,唯一 */
name varchar(50) not null UNIQUE,/* 元素的名称,唯一 */
type int not null, /* 类型 0- 单据操作 1- 报表操作 2- 功能操作 */
bcode varchar(20) null, /* 对应操作的单据 / 报表 / 功能的代码 */
style int null, /* 单据:类型 0- 查看 1- 新增 2- 修改 3- 删除 */
/* 报表:无 */
/* 功能:无 */
term ntext null, /* 单据:查看 / 修改 / 删除时要符合的条件 , 如 "{$ 承揽合同 . 编号 }=12/n{$ 承揽合同 . 名称 }<>'afd'"*/
primary key(code)
)
go
drop table t_role
go
create table t_role /* 角色表 */
(
name varchar(30) not null,
category varchar(50) null,
remark varchar(100) null,
primary key(name)
)
go
drop table t_roleelement
go
create table t_roleelement /* 角色元素操作表 */
(
rname varchar(30) not null, /* 角色名称 */
ecode varchar(20) not null, /* 元素的代码 */
primary key(rname,ecode)
)
go
drop table t_users
go
create table t_users /* 用户表 */
(
name varchar(20) not null, /* 用户的名称 */
dcode varchar(20) not null, /* 所属的部门 */
category varchar(50) null, /* 用户的类别 */
pswd varchar(15) null, /* 密码 */
primary key(name)
)
go
/* 插入系统管理员 */
INSERT INTO t_users
(
name,
dcode,
category,
pswd
)
VALUES
(
'Admini',
'system',
' 超级用户 ',
''
)
go
drop table t_userrole
go
create table t_userrole /* 用户角色表 */
(
uname varchar(20) not null, /* 用户名称 */
rname varchar(30) not null, /* 角色的名称 */
primary key(uname,rname)
)
go
INSERT INTO t_userrole
(
uname,
rname
)
VALUES
(
'Admini',
' 系统管理员 '
)
go
drop table t_dept
go
create table t_dept /* 部门表 */
(
code varchar(20) not null, /* 部门的代码 */
name varchar(50) not null UNIQUE,/* 部门的名称 */
type varchar(10) null, /* 部门的类别 行政 仓库 车间 */
subtype varchar(16) null, /* 子类别 成品仓库 原料仓库自定义 */
primary key(code)
)
go
/* 插入系统管理部 */
INSERT INTO t_dept
(
code,
name,
type
)
VALUES
(
'system',
' 系统管理部 ',
' 行政 '
)
go
(
code varchar(20) not null, /* 元素的代码,唯一 */
name varchar(50) not null UNIQUE,/* 元素的名称,唯一 */
type int not null, /* 类型 0- 单据操作 1- 报表操作 2- 功能操作 */
bcode varchar(20) null, /* 对应操作的单据 / 报表 / 功能的代码 */
style int null, /* 单据:类型 0- 查看 1- 新增 2- 修改 3- 删除 */
/* 报表:无 */
/* 功能:无 */
term ntext null, /* 单据:查看 / 修改 / 删除时要符合的条件 , 如 "{$ 承揽合同 . 编号 }=12/n{$ 承揽合同 . 名称 }<>'afd'"*/
primary key(code)
)
go
drop table t_role
go
create table t_role /* 角色表 */
(
name varchar(30) not null,
category varchar(50) null,
remark varchar(100) null,
primary key(name)
)
go
drop table t_roleelement
go
create table t_roleelement /* 角色元素操作表 */
(
rname varchar(30) not null, /* 角色名称 */
ecode varchar(20) not null, /* 元素的代码 */
primary key(rname,ecode)
)
go
drop table t_users
go
create table t_users /* 用户表 */
(
name varchar(20) not null, /* 用户的名称 */
dcode varchar(20) not null, /* 所属的部门 */
category varchar(50) null, /* 用户的类别 */
pswd varchar(15) null, /* 密码 */
primary key(name)
)
go
/* 插入系统管理员 */
INSERT INTO t_users
(
name,
dcode,
category,
pswd
)
VALUES
(
'Admini',
'system',
' 超级用户 ',
''
)
go
drop table t_userrole
go
create table t_userrole /* 用户角色表 */
(
uname varchar(20) not null, /* 用户名称 */
rname varchar(30) not null, /* 角色的名称 */
primary key(uname,rname)
)
go
INSERT INTO t_userrole
(
uname,
rname
)
VALUES
(
'Admini',
' 系统管理员 '
)
go
drop table t_dept
go
create table t_dept /* 部门表 */
(
code varchar(20) not null, /* 部门的代码 */
name varchar(50) not null UNIQUE,/* 部门的名称 */
type varchar(10) null, /* 部门的类别 行政 仓库 车间 */
subtype varchar(16) null, /* 子类别 成品仓库 原料仓库自定义 */
primary key(code)
)
go
/* 插入系统管理部 */
INSERT INTO t_dept
(
code,
name,
type
)
VALUES
(
'system',
' 系统管理部 ',
' 行政 '
)
go
分为几个概念:
1.User—— 用户
2.UserGroup—— 用户组
3.Permission—— 权限项
4.Project—— 项目
5.State—— 状态,一个项目可有 n 个状态组成
6.Process—— 处理项,从属于 State ,即一个 State 中可进行多种处理
7.Role—— 用户角色
权限方面的思路就是将 State 与 Role 绑定,将 User 加入到 Role 中,通过 Role-State
关系判断用户是否有进入 State 操作 Process 的权限。
因为设计出发点是一个便于开发的框架,所以开发的过程大致是这样的:
a. 分析业务需求,用一个 Project 进行描述
b. 将 Project 的完成分成几个步骤,每个步骤对应一个 State
c. 确定每个 State 有哪些处理要求,构建 Process
d. 分析用户权限,定义 Role ,并为每个 State 确定 Role-State 关系
e. 将 User 加入 Role
也许有网友认为 Role 的功能与 UserGroup 重复,而 Permission 没有使用到。我的考虑
是这样的:
实施 HARVEST 时,我曾遇到这样一个问题, HARVEST 将使用者分为管理员和用户两类,
用户可通过设定角色无穷分类,而管理员确无法细化。为此我认为在角色之上,应该从
系统级上进行一个用户分类,即 UserGroup 。 UserGroup 是系统识别的,对用户透明。
Permission 的考虑也类似, Permision 与 User 、 UserGroup 都可建立联系,目的在于让系
统在方法调用时可判断识别,一般我用 facade 模式构建一个个模块,模块入口类统一控
制 Permission 。这样的设计是为了使用户在定义权限时简单化,只要定义将不同的 User
加入到不同的 Role 就行,同时也避免系统因为用户权限使用的过于简单,导致系统级权
限管理的弱化。
1.User—— 用户
2.UserGroup—— 用户组
3.Permission—— 权限项
4.Project—— 项目
5.State—— 状态,一个项目可有 n 个状态组成
6.Process—— 处理项,从属于 State ,即一个 State 中可进行多种处理
7.Role—— 用户角色
权限方面的思路就是将 State 与 Role 绑定,将 User 加入到 Role 中,通过 Role-State
关系判断用户是否有进入 State 操作 Process 的权限。
因为设计出发点是一个便于开发的框架,所以开发的过程大致是这样的:
a. 分析业务需求,用一个 Project 进行描述
b. 将 Project 的完成分成几个步骤,每个步骤对应一个 State
c. 确定每个 State 有哪些处理要求,构建 Process
d. 分析用户权限,定义 Role ,并为每个 State 确定 Role-State 关系
e. 将 User 加入 Role
也许有网友认为 Role 的功能与 UserGroup 重复,而 Permission 没有使用到。我的考虑
是这样的:
实施 HARVEST 时,我曾遇到这样一个问题, HARVEST 将使用者分为管理员和用户两类,
用户可通过设定角色无穷分类,而管理员确无法细化。为此我认为在角色之上,应该从
系统级上进行一个用户分类,即 UserGroup 。 UserGroup 是系统识别的,对用户透明。
Permission 的考虑也类似, Permision 与 User 、 UserGroup 都可建立联系,目的在于让系
统在方法调用时可判断识别,一般我用 facade 模式构建一个个模块,模块入口类统一控
制 Permission 。这样的设计是为了使用户在定义权限时简单化,只要定义将不同的 User
加入到不同的 Role 就行,同时也避免系统因为用户权限使用的过于简单,导致系统级权
限管理的弱化。
问题:
怎么解决人员的多角色的问题,例如:一个员工既是财务总监又是经理,就是又多种角色的人,你们是怎么解决权限问题
不涉及角色交叉,如果一定要涉及的的话,取权限并集进行权限控制