在企业开发中,往往对权限的控制有较高的要求,其中也经常会被要求实现对业务数据操作进行权限控制。在这里,分享一下本人在企业开发中的数据权限的实现经验。本文所用的方法和实例,可在CSDN的代码托管平台找到。需要的童鞋可点击:https://code.csdn.net/xuanbg/starx-bip自行查看或下载。要想管理数据权限,首先得定义数据的归属和用户对数据的访问范围,和功能权限相比,这一点是数据权限特有的。那么数据的归属要怎么定义呢?一般来说,可在数据的生产者和数据的生产部门这两个维度进行定义。例如:
序号 收款金额 收款部门 收款人
1 3000.00 A部门 张三
2 2000.00 B部门 李四
3 50000.00 A部门 王五
从上表可以看出来,第一行数据中的3000元是A部门的张三收取的,而第二行数据中的2000元则是B部门的李四收取。如果授权给张三只能看到他自己的业务数据,那么对于张三来说,第二行和第三行数据就不应该看到。如果授权王五能够看到A部门的业务数据,则王五能看到的数据是第一行和第三行。如果授权李四能够管理整个公司的收款业务数据,那么李四应该可以读取上表里的所有数据。
这里就又涉及到了部门、机构这些概念,这里且不深谈。一般来说,业务数据的权限类型是只读和读写两种,数据的访问范围则稍复杂,有如下这么几种:
1、仅本人,意味着用户只能访问其本人产生的业务数据
2、仅本部门,意味着用户可以访问其所在部门所有用户产生的业务数据
3、本部门全部,意味着用户不但可以访问其所在部门所有用户产生的业务数据,还可以访问其所在部门下属部门所有用户产生的业务数据
4、本机构全部,意味着用户可以访问其所在机构(通常是整个分子公司)所有的业务数据
5、整个公司,意味着用户可以获取包括母公司及母公司其它分子公司的全部数据
6、自定义,用户可以以不同的访问权限访问指定部门的业务数据,譬如A部门只读,B部门可读写。
涉及权限,无非是:角色、角色成员和角色权限。功能权限如此,数据权限也没有区别。在数据模型的设计上,就会有如下结构:
角色表:Id,角色名称,以及其它字段
角色成员表:Id,角色Id,成员Id
角色数据权限表:Id,角色Id,数据表名或Id,访问范围,访问权限
以上三张表结合起来,我们就可以知道当前登录用户能访问哪些数据表以及只读还是读写权限了。但是仅仅知道这些还是不够的,我们需要一种高效的方法来对数据进行筛选,以便用户能够获取且仅获取他能够获取的数据。然后在每条数据上附加上权限标签来告诉系统这条数据用户是只读还是读写。
在获取数据前,我们首先需要知道特定的数据表(这个由业务模块决定,不同的业务必然使用不同的数据表),当前用户被授权访问什么范围的数据。由于用户可能是多个角色的成员,所以这个需要通过一个表值函数来合并所有角色权限,从而获取当前用户的真正权限。函数源代码如下:
/*****表值函数:获取当前登录用户有效数据访问范围类型和权限*****/
IF EXISTS (SELECT * FROM sysobjects WHERE id = OBJECT_ID(N'User_DataPerm') AND OBJECTPROPERTY(id, N'ISTABLEFUNCTION') = 1)
DROP FUNCTION User_DataPerm
GO
CREATE FUNCTION User_DataPerm(
@ModuleId VARCHAR(36), --模块Id
@UserId VARCHAR(36), --当前登录用户Id
@OrgId VARCHAR(36) --当前登录部门Id
)
RETURNS TABLE AS
RETURN
select D.TableId, max(D.Permission) as Permission, D.Mode
from(
select R.RoleId
from Sys_Role_User R
where R.UserId = @UserId
union
select R.RoleId
from Sys_Role_UserGroup R
join Sys_UserGroupItem G on G.GroupId = R.GroupId
and G.UserId = @UserId
union
select R.RoleId
from Sys_Role_Position R
join Sys_User_Org P on P.OrgId = R.OrgId
and P.UserId = @UserId
join Sys_Organization O on O.Id = R.OrgId
and O.ParentId = @OrgId
) R
join Sys_RolePerm_Data D on D.RoleId = R.RoleId
join Sys_ModuleTable M on M.Id = D.TableId
and M.ModuleId = @ModuleId
group by D.TableId, D.Mode
having max(D.Permission) > 0
GO
知道用户被授权的访问范围之后,1和2两种情况简单加个条件过滤就行了,3-6四种情况需要用到表值函数来获取具体的授权访问范围和访问权限。
/*****表值函数:获取当前登录用户数据访问范围和权限*****/
IF EXISTS (SELECT * FROM sysobjects WHERE id = OBJECT_ID(N'User_Data') AND OBJECTPROPERTY(id, N'ISTABLEFUNCTION') = 1)
DROP FUNCTION User_Data
GO
CREATE FUNCTION User_Data(
@ModuleId VARCHAR(36), --模块Id
@UserId VARCHAR(36), --当前登录用户Id
@OrgId VARCHAR(36) --当前登录部门Id
)
RETURNS @PermScope TABLE(
OrgId VARCHAR(36),
Permission INT
) AS
BEGIN
DECLARE @Table VARCHAR(36)
DECLARE @Code VARCHAR(36)
DECLARE @Mode INT
DECLARE @Permission INT
select @Table = TableId, @Mode = Mode, @Permission = Permission
from dbo.User_DataPerm(@ModuleId, @UserId, @OrgId)
if @Mode = 4
insert into @PermScope
select RDI.OrgId, RDI.Permission
from Sys_RolePerm_DataItem RDI
join Sys_RolePerm_Data RD on RD.Id = RDI.PermDateId
and RD.TableId = @Table
else
begin
if @Mode = 3
select @OrgId = dbo.GetOrgId(@OrgId, 0)
if @Mode = 2
select @OrgId = dbo.GetOrgId(@OrgId, 1)
select @Code = Code from Sys_Organization where Id = @OrgId
insert into @PermScope
select Id, @Permission
from Sys_Organization
where Code like @Code + '%'
end
RETURN
END
GO
/*****标量值函数:获取上级或根机构Id*****/
IF EXISTS (SELECT * FROM sysobjects WHERE id = OBJECT_ID(N'GetOrgId') AND OBJECTPROPERTY(id, N'ISSCALARFUNCTION') = 1)
DROP FUNCTION GetOrgId
GO
CREATE FUNCTION GetOrgId (
@DeptId VARCHAR(36), --部门Id
@Type INT --机构类型:0、根机构;1、上级机构
)
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_Organization where Id = @DeptId
if @ParentId is null
set @NodeType = 1
if @NodeType != 1
set @DeptId = @ParentId
end
RETURN(@DeptId)
END
GO