转自
http://blog.csdn.net/xuanbg/article/details/23286717
1 引言
权限,可分为“功能(操作)权限”和数据权限两种,在系统中,两种权限应当同时有效。例如,在windows系统中,某用户具有新建一个文件的功能权限,该用户在C盘没有写权限,但在D盘有写权限;则该用户不能把他创建的文件保存在C盘而只能保存在D盘。
在上述例子中,能否创建文件是由功能权限来控制的,能否保存文件是由数据权限进行控制的。只有两者同时有效,用户的业务才能顺利进行。
简单地说,权限管理就是对资源的管理。权限管理的目的就是建立分配资源的规则,以便用户能够通过这套规则,获取他们应该获得的资源。
1.1 定义
² 功能权限:
也叫操作权限,指的是允许或拒绝用户使用系统提供的某个功能。
² 数据权限:
指的是允许或拒绝用户进行某个数据的增删改查操作。
² 授权:
指的是分配具体的权限给具体的人。
² 鉴权:
指的是对具体人的行为,根据权限规则进行合法性鉴别。
1.2 授权的基本原则
对于授权来说,需要定义的有且只有权限和授权对象两个要素。简而述之,对于功能操作就是“什么功能授权给哪个用户来操作”。同样,对于数据,就是“什么数据授权给哪个用户来操作”。
一般情况下,我们并不会对单一的功能/数据进行单用户的授权管理,因为这样用户操作起来显然非常麻烦。为了方便和简化操作,一般的授权规则是:
哪些功能/数据授权给哪些用户
1.3 授权的一般方法
在实际的授权管理中,我们总是根据业务的需求,将一些在业务上不可分割的、需要允许用户一起使用的功能,组合成一个权限集合进行统一授权。对于这样的权限集合,我们一般称之为“角色”。也就是说,我们通过角色来定义用户被允许使用哪些功能和访问哪些数据。当然,我们一般把功能和数据分开来进行授权,以便获得更加灵活的权限规则配置方法,以适应更广泛的授权需求。
由于某些不同用户在该业务上需要具有相同的权限,那么这些不同的用户在特定的业务上就具有了共性,可以作为一个抽象的用户来进行权限的授予。授权管理使用的抽象的用户,也就是用户集合,除了普遍使用的“用户组”外,还可以引用别的业务中所使用的对象。例如组织机构管理中的“机构/部门”、“职位”和工作流中使用的“岗位”等,在授权管理中都是作为用户集合使用,本质毫无二致。
通过让抽象的用户扮演角色,即可使这个抽象的用户所代表的真实用户获得完成业务所需的权限。通过这样的方式,可以简化授权管理,方便用户操作。
2 授权管理
将特定的权限授予特定的人群的过程,我们称之为“授权”。为了能够方便地进行授权操作,我们必须要有一个能够提供合理授权方法的用户界面。
2.1 功能权限的授权
对于一个可扩展的系统来说,意味着功能是不断变化的。为了适应这种不能事先确定的变化,必须将功能权限进行分散管理。分散管理的好处如下:
² 天然地支持业务的动态变化,系统实现简单。
² 权限的调整范围可控制在局部范围,方便权限的管理和操作。
2.1.1 一般权限的授予
功能权限是单维度的,可以通过简单的在功能列表或功能树上进行勾选来确定一个“角色”所允许的功能操作。然后,让相应用户成为该“角色”的“扮演者”。这样就可以把该角色所允许的功能操作授权给指定的用户了。
如果需要有更多的不同角色,那么新建角色,勾选不同的被允许的功能操作,并分别让相应的用户成为新角色的成员即可。
2.1.2 例外权限的授予
如果某个特定用户张三需要额外的一个权限,那么新建一个允许该功能操作的角色,并让张三成为该角色成员即可使张三拥有额外的权限。
如果某个特定用户李四需要比同一项目组的其他人少一个权限,那么新建一个拒绝该功能操作的角色,并让李四成为该角色成员即可使李四不能进行该项操作。因为在鉴权过程中,拒绝的优先级要高于允许。
2.2 数据权限的授权
数据在系统中共同的特性有如下维度:
² 业务维度:不同的业务产生不同的数据
² 生产者维度:相同的业务会有多个数据生产者和生产部门
有些业务需要用户访问其他业务的数据,或者是其他数据生产者中特定生产者的生产的数据。简单的说,就是数据权限的授予必须支持跨业务和跨部门。
2.2.1 一般权限的授予
由于数据的特殊性质,实际上在有限范围内的授权比功能权限的授予更加方便。因为生产部门和生产者具有天然的分类属性,所以象“本机构”、“本部门”、“本人”这些对生产者维度的进一步抽象就有了用武之地。
在不对业务维度做限定的情况下,就可以配置例如“允许本部门的成员管理(增删改查)本部门的数据”这样的权限规则。那么对于不同部门的用户,这条共同的规则所产生的效果并不相同,具体的效果是与用户所在的部门的业务和产生的数据相对应的。
根据以上分析,我们可以内置一些抽象规则,例如:
² 允许管理本机构(含下级机构/部门)的数据
² 允许管理本部门(含下级部门)的数据
² 仅允许管理本部门的数据
² 仅允许管理本人的数据
² 允许查看本机构(含下级机构/部门)的数据
² 允许查看本部门(含下级部门)的数据
² 仅允许查看本部门的数据
² 仅允许查看本人的数据
2.2.2 自定义权限的授予
一般数据权限的授予只能局限于符合高度抽象规则所限定的范围。如果要在这个范围之外的数据进行授权,例如想让财务部的人访问采购部的数据,显然是一般数据授权所不能支持的。
这个时候,我们就必须要提供用户在角色中自由定义允许或禁止用户访问的数据集的方法。上面说过,一个数据需要在业务和生产者两个维度上进行描述,才能确定数据。那么在定义角色所允许访问的数据集时,因为授权分散在不同的业务中,所以业务是确定的,剩下的就是需要指定一个或多个生产者。在这里,生产者可以使用抽象方法进行归纳分类。
最后,类同于功能权限的授权方式,我们把定义好的角色分配给一个抽象的用户即可将一个自定义的数据权限授予抽象用户代表的真实用户。
3 权限管理的实现
权限管理的具体实现方法,离不开数据结构的支持。相对来说,有具体的数据结构,我们也更容易理解权限的管理机制。在讨论权限之前,我们还需要先了解权限管理的对象:功能资源和数据资源。这些资源同样需要一个数据结构去进行定义。
3.1 组织机构和用户
组织机构表:
- CREATE TABLE Sys_Organization(
- [ID] VARCHAR(36) PRIMARY KEY NONCLUSTERED DEFAULT NEWID(),
- [SN] BIGINT CONSTRAINT IX_Sys_Organization UNIQUE CLUSTERED IDENTITY(1,1),
- [ParentId] VARCHAR(36),
- [NodeType] INT NOT NULL,
- [Index] INT,
- [Code] VARCHAR(32),
- [Name] NVARCHAR(32) NOT NULL,
- [Alias] NVARCHAR(16),
- [FullName] NVARCHAR(32),
- [PostId] VARCHAR(36),
- [Validity] BIT DEFAULT 0 NOT NULL,
- [CreatorUserId] VARCHAR(36),
- [CreateTime] DATETIME DEFAULT GETDATE() NOT NULL
- )
- GO
用户表:
- CREATE TABLE Sys_User(
- [ID] VARCHAR(36) PRIMARY KEY NONCLUSTERED,
- [SN] BIGINT CONSTRAINT IX_Sys_User UNIQUE CLUSTERED IDENTITY(1,1),
- [Name] NVARCHAR(16) NOT NULL,
- [LoginName] VARCHAR(36) NOT NULL,
- [Password] VARCHAR(32) DEFAULT 'e10adc3949ba59abbe56e057f20f883e' NOT NULL,
- [Description] NVARCHAR(MAX),
- [BuiltIn] BIT DEFAULT 0 NOT NULL,
- [Validity] BIT DEFAULT 0 NOT NULL,
- [CreatorUserId] VARCHAR(36),
- [CreateTime] DATETIME DEFAULT GETDATE() NOT NULL
- )
- GO
3.2 资源
3.2.1 模块和功能
模块表:
- CREATE TABLE Sys_Module(
- [ID] VARCHAR(36) PRIMARY KEY NONCLUSTERED DEFAULT NEWID(),
- [SN] BIGINT CONSTRAINT IX_Sys_Module UNIQUE CLUSTERED IDENTITY(1,1),
- [ParentId] VARCHAR(36),
- [Level] INT NOT NULL,
- [Name] NVARCHAR(64) NOT NULL,
- [Location] VARCHAR(MAX) NOT NULL,
- [DataTable] NVARCHAR(64),
- [Description] NVARCHAR(MAX),
- [RegisterTime] DATETIME DEFAULT GETDATE() NOT NULL
- )
- GO
模块功能表:
- CREATE TABLE Sys_ModuleAction(
- [ID] VARCHAR(36) PRIMARY KEY NONCLUSTERED DEFAULT NEWID(),
- [SN] BIGINT CONSTRAINT IX_Sys_ModuleAction UNIQUE CLUSTERED IDENTITY(1,1),
- [ModuleId] VARCHAR(36) FOREIGN KEY REFERENCES Sys_Module(ID) ON DELETE CASCADE NOT NULL,
- [Name] NVARCHAR(64) NOT NULL,
- [SubModuleId] VARCHAR(36) FOREIGN KEY REFERENCES Sys_Module(ID),
- [Description] NVARCHAR(MAX)
- )
- GO
3.2.2 业务数据
物资数据表:
- CREATE TABLE MDG_Material(
- [MID] VARCHAR(36) PRIMARY KEY NONCLUSTERED FOREIGN KEY REFERENCES MasterData(ID) ON DELETE CASCADE,
- [SN] BIGINT CONSTRAINT IX_MDG_Material UNIQUE CLUSTERED IDENTITY(1,1),
- [Index] INT,
- [BarCode] VARCHAR(16),
- [Brand] NVARCHAR(16),
- [Model] NVARCHAR(32),
- [Size] DECIMAL(20,6),
- [SizeType] VARCHAR(36) FOREIGN KEY REFERENCES MasterData(ID),
- [Color] NVARCHAR(8),
- [Material] NVARCHAR(8),
- [StorageType] VARCHAR(36) FOREIGN KEY REFERENCES MasterData(ID),
- [Description] NVARCHAR(MAX),
- [Enable] BIT DEFAULT 1 NOT NULL,
- [CreatorDeptId] VARCHAR(36) FOREIGN KEY REFERENCES Sys_Organization(ID),
- [CreatorUserId] VARCHAR(36) FOREIGN KEY REFERENCES Sys_User(ID) NOT NULL,
- [CreateTime] DATETIME DEFAULT GETDATE() NOT NULL
- )
- GO
3.3 RBAC模型
3.3.1 角色
角色表:
- CREATE TABLE Sys_Role(
- [ID] VARCHAR(36) PRIMARY KEY NONCLUSTERED DEFAULT NEWID(),
- [SN] BIGINT CONSTRAINT IX_Sys_Role UNIQUE CLUSTERED IDENTITY(1,1),
- [Name] NVARCHAR(64) NOT NULL,
- [Description] NVARCHAR(MAX),
- [BuiltIn] BIT DEFAULT 0 NOT NULL,
- [CreatorUserId] VARCHAR(36) FOREIGN KEY REFERENCES Sys_User(ID) NOT NULL,
- [CreateTime] DATETIME DEFAULT GETDATE() NOT NULL
- )
- GO
3.3.2 角色成员
角色成员(用户)表:
- CREATE TABLE Sys_Role_User(
- [ID] VARCHAR(36) PRIMARY KEY NONCLUSTERED DEFAULT NEWID(),
- [SN] BIGINT CONSTRAINT IX_Sys_Role_User UNIQUE CLUSTERED IDENTITY(1,1),
- [RoleId] VARCHAR(36) FOREIGN KEY REFERENCES Sys_Role(ID) ON DELETE CASCADE NOT NULL,
- [UserId] VARCHAR(36) FOREIGN KEY REFERENCES Sys_User(ID) NOT NULL,
- [CreatorUserId] VARCHAR(36) FOREIGN KEY REFERENCES Sys_User(ID) NOT NULL,
- [CreateTime] DATETIME DEFAULT GETDATE() NOT NULL
- )
- GO
3.3.3 角色权限
角色权限表:
- CREATE TABLE Sys_RolePerm_Module(
- [ID] VARCHAR(36) PRIMARY KEY NONCLUSTERED DEFAULT NEWID(),
- [SN] BIGINT CONSTRAINT IX_Sys_RolePerm_Module UNIQUE CLUSTERED IDENTITY(1,1),
- [RoleId] VARCHAR(36) FOREIGN KEY REFERENCES Sys_Role(ID) ON DELETE CASCADE NOT NULL,
- [ModuleId] VARCHAR(36) FOREIGN KEY REFERENCES Sys_Module(ID) ON DELETE CASCADE NOT NULL,
- [Action] INT DEFAULT 0 NOT NULL,
- [Mode] INT DEFAULT 0 NOT NULL,
- [Permission] INT DEFAULT 0 NOT NULL,
- [CreatorUserId] VARCHAR(36) FOREIGN KEY REFERENCES Sys_User(ID) NOT NULL,
- [CreateTime] DATETIME DEFAULT GETDATE() NOT NULL
- )
- GO
3.4 如何获取用户的权限
获取指定用户的功能权限需要先获取该用户可以访问的模块,此功能我们可以使用一个数据库表值函数来返回指定用户被授权访问的模块ID列表。在用户启动某一模块时,使用一个数据库表值函数来返回被授权功能的ID列表。
在用户访问数据时,我们需要对用户访问的数据根据授权情况进行过滤,并为这些数据加上权限标记,以便告知系统用户被许可的操作方式(只读/读写)。
我们可以在数据访问层前端进行数据的过滤和标记,这种方式的优点是:
² 安全,外部没有注入、篡改的机会。
² 高效,数据访问层获取的数据已经经过筛选,不会返回无效的数据。
² 兼容性好,和应用系统完全无关,即使应用系统通过存储过程处理数据,也能完全兼容。
简单地来说,这种机制只需要你在获取数据的时候使用inner join一个表值函数,输入函数的参数(模块ID,登录部门ID,用户ID)即可。
4 数据库函数
4.1 功能权限函数
4.1.1 获取授权模块
- /*****表值函数:获取当前登录用户允许访问模块*****/
-
- CREATE FUNCTION Get_PermModule(
- @UserId VARCHAR(36),
- @OrgId VARCHAR(36)
- )
-
- RETURNS TABLE AS
-
- RETURN
- with Roles as(
- select R.RoleId
- from Sys_Role_User R
- where R.UserId = @UserId
- union
- select R.RoleId
- from Sys_Role_UserGroupR
- join Sys_UserGroupMemberG on G.GroupId = R.GroupId
- and G.UserId = @UserId
- union
- select R.RoleId
- from Sys_Role_PositionR
- join Sys_User_Org P on P.OrgId = R.OrgId
- and P.UserId = @UserId
- join Sys_OrganizationO on O.ID = R.OrgId
- and O.ParentId = @OrgId
- union
- select R.RoleId
- from Sys_Role_PositionR
- join Sys_User_Org P on P.OrgId = R.OrgId
- and P.UserId = @UserId
- join Sys_OrganizationO on O.ID = R.OrgId
- join Sys_OrgMerger OM on OM.MergerOrgId = O.ParentId
- and OM.OrgId = @OrgId
- )
-
- select M.ModuleId from Roles R
- join Sys_RolePerm_ModuleM on M.RoleId = R.RoleId
- group by M.ModuleId
- having min(M.Action) > 0
-
- GO
4.1.2 获取授权功能
- CREATE FUNCTION Get_PermAction(
- @ModuleId VARCHAR(36),
- @UserId VARCHAR(36),
- @OrgId VARCHAR(36)
- )
-
- RETURNS TABLE AS
-
- RETURN
- with Roles as(
- select R.RoleId
- from Sys_Role_User R
- where R.UserId = @UserId
- union
- select R.RoleId
- from Sys_Role_UserGroupR
- join Sys_UserGroupMemberG on G.GroupId = R.GroupId
- and G.UserId = @UserId
- union
- select R.RoleId
- from Sys_Role_PositionR
- join Sys_User_Org P on P.OrgId = R.OrgId
- and P.UserId = @UserId
- join Sys_OrganizationO on O.ID = R.OrgId
- and O.ParentId = @OrgId
- union
- select R.RoleId
- from Sys_Role_PositionR
- join Sys_User_Org P on P.OrgId = R.OrgId
- and P.UserId = @UserId
- join Sys_OrganizationO on O.ID = R.OrgId
- join Sys_OrgMerger OM on OM.MergerOrgId = O.ParentId
- and OM.OrgId = @OrgId
- )
-
- select A.ActionId from Roles R
- join Sys_RolePerm_ActionA on A.RoleId = R.RoleId
- join Sys_ModuleActionM on M.ID = A.ActionId
- and M.ModuleId = @ModuleId
- group by A.ActionId
- having min(A.Action) > 0
-
- GO
4.2 数据权限函数
4.2.1 获取授权数据(部门模式)
- CREATE FUNCTION DataPerm_Org(
- @ModuleId VARCHAR(36),
- @UserId VARCHAR(36),
- @OrgId VARCHAR(36)
- )
-
- RETURNS @PermScope TABLE(
- OrgId VARCHAR(36),
- Permission INT
- ) AS
-
- BEGIN
- DECLARE @Mode INT
- DECLARE @Permission INT
-
- select @Mode = Mode, @Permission = Permission
- from Get_PermData(@ModuleId, @UserId, @OrgId)
-
- if @Mode = 0
- insert into @PermScope
- select @OrgId, @Permission
- union
- select MergerOrgId, @Permission
- from Sys_OrgMerger
- where OrgId = @OrgId
-
- else if @Mode between 1 and 3
- begin
- if @Mode = 2
- select @OrgId = dbo.Get_SupOrg(@OrgId, 1)
- if @Mode = 3
- select @OrgId = dbo.Get_SupOrg(@OrgId, 0)
- insert into @PermScope
- select ID, @Permission from Get_SubOrg(@OrgId)
- end
-
- else if @Mode = 4
- insert into @PermScope
- select DC.OrgId, max(DC.Permission)
- from Sys_RolePerm_DataCustomDC
- join Sys_RolePerm_ModuleM on M.ID = DC.Perm_ModuleId
- and M.ModuleId = @ModuleId
- join Sys_OrganizationO on O.ID = DC.OrgId
- and O.Validity = 1
- group by DC.OrgId
- union
- select OM.MergerOrgId, max(DC.Permission)
- from Sys_RolePerm_DataCustomDC
- join Sys_RolePerm_ModuleM on M.ID = DC.Perm_ModuleId
- and M.ModuleId = @ModuleId
- join Sys_OrgMerger OM on OM.OrgId = DC.OrgId
- group by OM.MergerOrgId
-
- insert into @PermScope
- select 'All', @Permission
-
- RETURN
- END
- GO
4.2.2 获取授权数据(用户模式)
- CREATE FUNCTION DataPerm_User(
- @ModuleId VARCHAR(36),
- @UserId VARCHAR(36),
- @OrgId VARCHAR(36)
- )
-
- RETURNS @PermScope TABLE(
- UserId VARCHAR(36),
- Permission INT
- ) AS
-
- BEGIN
- DECLARE @Mode INT
- DECLARE @Permission INT
-
- select @Mode = Mode, @Permission = Permission
- from Get_PermData(@ModuleId, @UserId, @OrgId)
-
- if @Mode <0
- insert into @PermScope
- select @UserId, @Permission
-
- RETURN
- END
- GO
4.2.3 获取数据权限授权模式
- CREATE FUNCTION Get_PermData(
- @ModuleId VARCHAR(36),
- @UserId VARCHAR(36),
- @OrgId VARCHAR(36)
- )
-
- RETURNS TABLE AS
-
- RETURN
- with Roles as(
- select R.RoleId
- from Sys_Role_User R
- where R.UserId = @UserId
- union
- select R.RoleId
- from Sys_Role_UserGroupR
- join Sys_UserGroupMemberG on G.GroupId = R.GroupId
- and G.UserId = @UserId
- union
- select R.RoleId
- from Sys_Role_PositionR
- join Sys_User_Org P on P.OrgId = R.OrgId
- and P.UserId = @UserId
- join Sys_OrganizationO on O.ID = R.OrgId
- and O.ParentId = @OrgId
- union
- select R.RoleId
- from Sys_Role_PositionR
- join Sys_User_Org P on P.OrgId = R.OrgId
- and P.UserId = @UserId
- join Sys_OrganizationO on O.ID = R.OrgId
- join Sys_OrgMerger OM on OM.MergerOrgId = O.ParentId
- and OM.OrgId = @OrgId
- )
-
- select max(M.Permission) as Permission, max(M.Mode) as Mode from Roles R
- join Sys_RolePerm_ModuleM on M.RoleId = R.RoleId
- and M.ModuleId = @ModuleId
- group by M.ModuleId
-
- GO
4.3 其它函数
4.3.1 获取上级机构ID
- CREATE FUNCTION Get_SupOrg(
- @DeptId VARCHAR(36),
- @Type INT
- )
-
- RETURNS NVARCHAR(36) AS
- BEGIN
-
- DECLARE @NodeType INT = 0
- DECLARE @ParentId VARCHAR(36)
-
- while @NodeType !=1
- begin
- select @NodeType = NodeType * @Type, @ParentId = ParentId from Sys_Organizationwhere ID = @DeptId
- if @ParentId is null
- set @NodeType = 1
- if @NodeType != 1
- set @DeptId = @ParentId
- end
-
- RETURN @DeptId
- END
- GO
4.3.2 获取下属机构/部门ID
- CREATE FUNCTION Get_SubOrg(
- @OrgId VARCHAR(36)
- )
-
- RETURNS TABLE AS
-
- RETURN
- with
- OrgList as (
- select @OrgId as ID
- union all
- select O.ID from Sys_OrganizationO
- join OrgList L on L.ID = O.ParentId
- where Validity = 1
- and NodeType < 3),
- MergerOrg as(
- select OM.MergerOrgId as ID from OrgList OL
- join Sys_OrgMerger OM on OM.OrgId = OL.ID
- union all
- select O.ID from Sys_OrganizationO
- join MergerOrg M on M.ID = O.ParentId
- where Validity = 1
- and NodeType < 3)
-
- select ID from OrgList
- union
- select ID from MergerOrg
-
- GO