1. 程式人生 > >淺談資料許可權的實現方法和作用機制

淺談資料許可權的實現方法和作用機制

在企業開發中,往往對許可權的控制有較高的要求,其中也經常會被要求實現對業務資料操作進行許可權控制。在這裡,分享一下本人在企業開發中的資料許可權的實現經驗。本文所用的方法和例項,可在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


在函式裡還呼叫了另一個函式來獲取機構或根機構的Id,並以此為依據獲取該節點的編碼,只要把組織機構的編碼設計值為類似01、0101這樣的層級編碼,就很容易使用一個like來得到所有下屬節點的Id。最後,利用inner join 合併兩表內容並取交集的特性,把業務資料表和表值函式按組織機構Id 來一次inner join,就能把非授權範圍內的資料全部過濾掉,並且把所有業務資料加上訪問許可權的型別碼。