淺談資料許可權的實現方法和作用機制
要想管理資料許可權,首先得定義資料的歸屬和使用者對資料的訪問範圍,和功能許可權相比,這一點是資料許可權特有的。那麼資料的歸屬要怎麼定義呢?一般來說,可在資料的生產者和資料的生產部門這兩個維度進行定義。例如:
序號 收款金額 收款部門 收款人
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
在函式裡還呼叫了另一個函式來獲取機構或根機構的Id,並以此為依據獲取該節點的編碼,只要把組織機構的編碼設計值為類似01、0101這樣的層級編碼,就很容易使用一個like來得到所有下屬節點的Id。最後,利用inner join 合併兩表內容並取交集的特性,把業務資料表和表值函式按組織機構Id 來一次inner join,就能把非授權範圍內的資料全部過濾掉,並且把所有業務資料加上訪問許可權的型別碼。