.NET 通用高擴展性的細粒度權限管理架構(webApi/Mvc)


一. 權限場景分析:

1. 系統具有角色概念, 部門概念, 且都具有相應不同的權限

2. 用戶具有多個角色, 多個部門等關系, 並且能給單個用戶指派獨有的權限

3. 具有細粒度權限控制到資源的RBAC, 能控制頁面, 控制菜單, 控制邏輯, 控制單個操作, 控制到單一數據; 且具有一定的可擴展性

4. 適用於webapi/ mvc / wcf / webservice  混合項目中

5. 設置權限和驗證權限易用性高

 

 

二. 數據庫表設計

  1. 角色表

  2. 部門表

  3. 用戶表

  4. 菜單表

  5. 用戶表

  6. 權限表

  7. 用戶角色關系表

  8. 用戶部門關系表

  9. 菜單權限關系表

  10.用戶權限關系表

  11.部門權限關系表

  12. 權限匯總表

在一般正常的情況下, 大家伙都會有 菜單權限關系, 用戶權限關系, 部門權限關系等表, 在此處省去如上幾項權限關系表,  將如上權限表做成dll 當插件形式容入到項目中.

權限匯總表描述:

  既然將如上所有跟權限有關聯的關系表剔除后該如何來設計各種來自不同體系的權限呢? 因此在此權限匯總表中要有字段來定位到, 該條權限是屬於什么體系.

  因此權限匯總表中出現三個至關重要的字段: 

    1. PermissionType(權限的類型: 菜單,部門,角色,用戶 ); 

    2. PermissionReferenceId(權限類型的引用ID: 當Type為角色時 為角色的ID 以此類推); 

    3. PermissionAction(權限操作的ID: 每個權限將會划分為一個操作編號ID, 往后會深入講解)

 

 1 -- 權限匯總表
 2 CREATE TABLE `Permission` (  3   `Id` int(11) NOT NULL AUTO_INCREMENT,  4   `PermissionReferenceId` int(11) DEFAULT NULL COMMENT '權限引用ID(對應 角色ID, 用戶ID, 部門ID)',  5   `PermissionAction` int(11) DEFAULT NULL COMMENT '權限操作ID(每種資源的操作ID)',  6   `PermissionType` int(11) DEFAULT NULL COMMENT '權限類型()',  7   `CreateTime` datetime DEFAULT '1990-01-01 00:00:00' COMMENT '創建時間',  8   `UpdateTime` datetime DEFAULT '1990-01-01 00:00:00' COMMENT '修改時間',  9   `IsEnable` bit(1) DEFAULT b'1' COMMENT '是否可用', 10   PRIMARY KEY (`Id`) 11 ) ENGINE=InnoDB AUTO_INCREMENT=390 DEFAULT CHARSET=utf8 COMMENT='權限匯總表';
權限匯總表SQL代碼
 1 -- 部門表
 2 CREATE TABLE `AdminMent` (  3   `Id` int(11) NOT NULL AUTO_INCREMENT,  4   `DepartName` varchar(20) DEFAULT NULL COMMENT '部門名',  5   `Description` varchar(20) DEFAULT NULL COMMENT '部門說明',  6   `Icon` varchar(20) DEFAULT NULL COMMENT '圖標',  7   `CreateTime` datetime DEFAULT NULL COMMENT '創建時間',  8   `UpdateTime` datetime DEFAULT NULL COMMENT '修改時間',  9   `IsEnable` bit(1) DEFAULT b'1' COMMENT '是否可用', 10   PRIMARY KEY (`Id`) 11 ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='部門表'; 12 
13 
14 -- 部門用戶關系表
15 CREATE TABLE `AdminMentAdminUsers` ( 16   `AdminMent_Id` int(11) NOT NULL, 17   `AdminUser_Id` int(11) NOT NULL, 18   PRIMARY KEY (`AdminMent_Id`,`AdminUser_Id`), 19   KEY `AdminMent_UserCollection_Target` (`AdminUser_Id`) 20 ) ENGINE=MyISAM DEFAULT CHARSET=gbk; 21 
22 
23 -- 角色表
24 CREATE TABLE `AdminRole` ( 25   `Id` int(11) NOT NULL AUTO_INCREMENT, 26   `RoleName` varchar(20) DEFAULT NULL COMMENT '角色名', 27   `Description` varchar(20) DEFAULT NULL COMMENT '角色說明', 28   `CreateTime` datetime DEFAULT NULL COMMENT '創建時間', 29   `UpdateTime` datetime DEFAULT NULL COMMENT '修改時間', 30   `IsEnable` bit(1) DEFAULT b'1' COMMENT '是否可用', 31   PRIMARY KEY (`Id`) 32 ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COMMENT='后台角色表'; 33 
34 
35 -- 角色用戶關系表
36 CREATE TABLE `AdminRoleAdminUsers` ( 37   `AdminRole_Id` int(11) NOT NULL, 38   `AdminUser_Id` int(11) NOT NULL, 39   PRIMARY KEY (`AdminRole_Id`,`AdminUser_Id`), 40   KEY `AdminRole_UserCollection_Target` (`AdminUser_Id`) 41 ) ENGINE=MyISAM DEFAULT CHARSET=gbk; 42 
43 
44 -- 用戶表
45 CREATE TABLE `AdminUser` ( 46   `Id` int(11) NOT NULL AUTO_INCREMENT, 47   `UserName` varchar(20) DEFAULT NULL COMMENT '后台用戶名', 48   `Password` varchar(50) DEFAULT NULL COMMENT '后台用戶密碼', 49   `Mail` varchar(50) DEFAULT NULL COMMENT '用戶郵箱', 50   `Phone` varchar(20) DEFAULT NULL COMMENT '用戶手機', 51   `Description` varchar(20) DEFAULT NULL COMMENT '附加說明', 52   `CreateTime` datetime DEFAULT NULL COMMENT '創建時間', 53   `UpdateTime` datetime DEFAULT NULL COMMENT '修改時間', 54   `IsEnable` bit(1) DEFAULT b'1' COMMENT '是否可用', 55   PRIMARY KEY (`Id`) 56 ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COMMENT='后台用戶表'; 57 
58 -- 菜單表
59 CREATE TABLE `AdminMenu` ( 60   `Id` int(11) NOT NULL AUTO_INCREMENT, 61   `ParentsID` int(11) DEFAULT NULL COMMENT '父菜單ID', 62   `MenuName` varchar(20) DEFAULT NULL COMMENT '菜單名稱', 63   `Descriptotion` varchar(20) DEFAULT NULL COMMENT '菜單說明', 64   `ControllerPermissionName` varchar(20) DEFAULT NULL COMMENT '控制器名', 65   `ActionPermissionName` varchar(20) DEFAULT NULL COMMENT '操作器名', 66   `Sort` int(11) DEFAULT NULL COMMENT '排序', 67   `Icon` varchar(20) DEFAULT NULL COMMENT '圖標', 68   `CreateTime` datetime DEFAULT NULL COMMENT '創建時間', 69   `UpdateTime` datetime DEFAULT NULL COMMENT '修改時間', 70   `IsEnable` bit(1) DEFAULT b'1' COMMENT '是否可用', 71   PRIMARY KEY (`Id`) 72 ) ENGINE=InnoDB AUTO_INCREMENT=32 DEFAULT CHARSET=utf8 COMMENT='后台管理菜單表';
其它數據表SQL代碼

 

三. 權限無處不在

若要讓設置權限和驗證權限易用性高, 最好的解決方案則是利用 Attribute 來進行權限的設置.

如圖:

 

 1     /// <summary>
 2     /// 附加權限
 3     /// </summary>
 4     [AttributeUsage(validOn: AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
 5     public class PermissionAttribute : Attribute
 6     {
 7         /// <summary>
 8         /// 權限類型
 9         /// </summary>
10         public PermissionKinds Kind { get; set; } = PermissionKinds.AllowAnonymous;
11         /// <summary>
12         /// 操作權限
13         /// </summary>
14         public PermissionActions Action { get; set; } = PermissionActions.AllowAnonymous;
15     }
PermissionAttribute

 

將PermissionKinds 划分為 菜單 和 數據

 1     /// <summary>
 2     /// 用於標識api接口和mvc訪問權限驗證類型
 3     /// </summary>
 4     public enum PermissionKinds : int
 5     {
 6         /// <summary>
 7         /// 匿名
 8         /// </summary>
 9         AllowAnonymous = 0,
10         /// <summary>
11         /// 菜單
12         /// </summary>
13         MENU = 1,
14         /// <summary>
15         /// 資源操作
16         /// </summary>
17         DATASOURCE = 2
18     }
PermissionKinds

PermissionActions 則是 標識操作資源的權限編號

例:  用戶表: User_Read = 10, User_Edit=11, User_Add=12, User_Delete=13  (當然此處也可以進行擴展如: 搜索用戶 User_Search=14)

  1     /// <summary>
  2     /// 用於標識操作資源的權限動作類型
  3     /// </summary>
  4     [JsonConverter(typeof(int))]
  5     public enum PermissionActions : int
  6     {
  7         /// <summary>
  8         /// 匿名
  9         /// </summary>
 10         AllowAnonymous = 0,
 11 
 12 
 13         #region  表:ActionPermission(控制器操作權限表) 權限
 14         /// <summary>
 15         /// 控制器操作權限表讀取權限
 16         /// </summary>
 17         [PermissionActionDescription("控制器操作權限表", "讀取")]
 18         ActionPermission_Read = 10,
 19         /// <summary>
 20         /// 控制器操作權限表修改權限
 21         /// </summary>
 22         [PermissionActionDescription("控制器操作權限表", "修改")]
 23         ActionPermission_Edit = 11,
 24         /// <summary>
 25         /// 控制器操作權限表添加權限
 26         /// </summary>
 27         [PermissionActionDescription("控制器操作權限表", "添加")]
 28         ActionPermission_Add = 12,
 29         /// <summary>
 30         /// 控制器操作權限表刪除權限
 31         /// </summary>
 32         [PermissionActionDescription("控制器操作權限表", "刪除")]
 33         ActionPermission_Delete = 13,
 34         #endregion
 35 
 36         #region  表:AdminMent(部門表) 權限
 37         /// <summary>
 38         /// 部門表讀取權限
 39         /// </summary>
 40         [PermissionActionDescription("部門表", "讀取")]
 41         AdminMent_Read = 20,
 42         /// <summary>
 43         /// 部門表修改權限
 44         /// </summary>
 45         [PermissionActionDescription("部門表", "修改")]
 46         AdminMent_Edit = 21,
 47         /// <summary>
 48         /// 部門表添加權限
 49         /// </summary>
 50         [PermissionActionDescription("部門表", "添加")]
 51         AdminMent_Add = 22,
 52         /// <summary>
 53         /// 部門表刪除權限
 54         /// </summary>
 55         [PermissionActionDescription("部門表", "刪除")]
 56         AdminMent_Delete = 23,
 57         #endregion
 58 
 59         #region  表:AdminMenu(后台管理菜單表) 權限
 60         /// <summary>
 61         /// 后台管理菜單表讀取權限
 62         /// </summary>
 63         [PermissionActionDescription("后台管理菜單表", "讀取")]
 64         AdminMenu_Read = 30,
 65         /// <summary>
 66         /// 后台管理菜單表修改權限
 67         /// </summary>
 68         [PermissionActionDescription("后台管理菜單表", "修改")]
 69         AdminMenu_Edit = 31,
 70         /// <summary>
 71         /// 后台管理菜單表添加權限
 72         /// </summary>
 73         [PermissionActionDescription("后台管理菜單表", "添加")]
 74         AdminMenu_Add = 32,
 75         /// <summary>
 76         /// 后台管理菜單表刪除權限
 77         /// </summary>
 78         [PermissionActionDescription("后台管理菜單表", "刪除")]
 79         AdminMenu_Delete = 33,
 80         #endregion
 81 
 82         #region  表:AdminRole(后台角色表) 權限
 83         /// <summary>
 84         /// 后台角色表讀取權限
 85         /// </summary>
 86         [PermissionActionDescription("后台角色表", "讀取")]
 87         AdminRole_Read = 40,
 88         /// <summary>
 89         /// 后台角色表修改權限
 90         /// </summary>
 91         [PermissionActionDescription("后台角色表", "修改")]
 92         AdminRole_Edit = 41,
 93         /// <summary>
 94         /// 后台角色表添加權限
 95         /// </summary>
 96         [PermissionActionDescription("后台角色表", "添加")]
 97         AdminRole_Add = 42,
 98         /// <summary>
 99         /// 后台角色表刪除權限
100         /// </summary>
101         [PermissionActionDescription("后台角色表", "刪除")]
102         AdminRole_Delete = 43,
103         #endregion
104 
105         #region  表:AdminUser(后台用戶表) 權限
106         /// <summary>
107         /// 后台用戶表讀取權限
108         /// </summary>
109         [PermissionActionDescription("后台用戶表", "讀取")]
110         AdminUser_Read = 50,
111         /// <summary>
112         /// 后台用戶表修改權限
113         /// </summary>
114         [PermissionActionDescription("后台用戶表", "修改")]
115         AdminUser_Edit = 51,
116         /// <summary>
117         /// 后台用戶表添加權限
118         /// </summary>
119         [PermissionActionDescription("后台用戶表", "添加")]
120         AdminUser_Add = 52,
121         /// <summary>
122         /// 后台用戶表刪除權限
123         /// </summary>
124         [PermissionActionDescription("后台用戶表", "刪除")]
125         AdminUser_Delete = 53,
126         #endregion
127 
128         #region  表:AdminUserRoles(后台用戶角色表) 權限
129         /// <summary>
130         /// 后台用戶角色表讀取權限
131         /// </summary>
132         [PermissionActionDescription("后台用戶角色表", "讀取")]
133         AdminUserRoles_Read = 60,
134         /// <summary>
135         /// 后台用戶角色表修改權限
136         /// </summary>
137         [PermissionActionDescription("后台用戶角色表", "修改")]
138         AdminUserRoles_Edit = 61,
139         /// <summary>
140         /// 后台用戶角色表添加權限
141         /// </summary>
142         [PermissionActionDescription("后台用戶角色表", "添加")]
143         AdminUserRoles_Add = 62,
144         /// <summary>
145         /// 后台用戶角色表刪除權限
146         /// </summary>
147         [PermissionActionDescription("后台用戶角色表", "刪除")]
148         AdminUserRoles_Delete = 63,
149         #endregion
150 }
PermissionActions

 至此,我們只需要要將要 進行權限審核驗證的方法上進行 permissionattribute 標記即可

附上相應的codesmith 代碼生成

 1 <%@ CodeTemplate Language="C#" TargetLanguage="C#" Src="" Inherits="" Debug="False" Description="Template description here." ResponseEncoding="UTF-8" %>
 2 <%@ Property Name="SourceDatabase" Type="SchemaExplorer.DatabaseSchema" Default="" Description="源數據庫" %>
 3 <%@ Assembly Name="SchemaExplorer" %>
 4 <%@ Assembly Name="System.Data" %>
 5 <%@ Assembly Name="mscorlib" %>
 6 <%@ Import Namespace="SchemaExplorer" %>
 7 <%@ Import Namespace="System.Data" %>
 8 <%@ Import Namespace="System.Collections.Generic" %>
 9 
10 <%@ Assembly Src="..\CommonUtility.cs" %>
11 <%@ Import Namespace="Common" %>
12 
13 public enum PermissionActions : int
14 {
15     /// <summary>
16     /// 匿名
17     /// </summary>
18     AllowAnonymous = 0,
19     
20 <%
21 int index = 0;
22 foreach(var table in SourceDatabase.Tables){
23     var tableName = table.GetClassName();
24     var tableDescription = table.Description;
25     index +=10;
26     var count = index;
27 %>
28     #region  表:<%=tableName %>(<%=tableDescription%>) 權限
29     /// <summary>
30     /// <%=tableDescription%>讀取權限
31     /// </summary>
32     [PermissionActionDescription("<%=tableDescription%>","讀取")]
33     <%=tableName %>_Read = <%=count++%>,
34     /// <summary>
35     /// <%=tableDescription%>修改權限
36     /// </summary>
37     [PermissionActionDescription("<%=tableDescription%>","修改")]
38     <%=tableName %>_Edit = <%=count++%>,
39     /// <summary>
40     /// <%=tableDescription%>添加權限
41     /// </summary>
42     [PermissionActionDescription("<%=tableDescription%>","添加")]
43     <%=tableName %>_Add = <%=count++%>,
44     /// <summary>
45     /// <%=tableDescription%>刪除權限
46     /// </summary>
47     [PermissionActionDescription("<%=tableDescription%>","刪除")]
48     <%=tableName %>_Delete = <%=count++%>,
49     #endregion
50     
51 <%
52 }
53 %>
54  }
Codesmith權限數據庫表生成權限Enum(PermissionActions)

 

 

四. 權限認證

1. 在用戶登錄時,將用戶所在的所有角色組權限, 和部門權限全部查詢出來合並,並添加到緩存中.

2. 當用戶在訪問指定的資源或action方法前 進行驗證其是否具有相應權限

 

實施方案:

1.webapi

  新建 Filter 實現 IAuthorizationFilter 接口  

 1 public Task<HttpResponseMessage> ExecuteAuthorizationFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
 2 {
 3     var permissions_on_action = actionContext.ActionDescriptor.GetCustomAttributes<PermissionAttribute>().ToList();
 4     
 5     bool isAnonymous = actionContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Count() > 0
 6                                     || permissions_on_action.Any(a => a.Action == PermissionActions.AllowAnonymous)
 7                                     ;
 8     
 9     if (isAnonymous)
10         return continuation.Invoke();
11     else
12     {
13         //該 操作所需要的所有權限
14         var action_needs_permissions = permissions_on_action.Select(s => s.Action).ToArray();
15         if (action_needs_permissions.All(a => session.PermissionsArray.Contains(a)))
16         {
17             isAuthor = true;
18         }
19         else if (action_needs_permissions.Count() == 0)
20         {
21             isAuthor = true;
22         }
23                     
24 
25         if (!isAuthor)
26         {
27             var result = new ResultModel(Code.Unauthorized, "沒有足夠的權限操作資源");
28             actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.OK) { Content = new StringContent(result.ToJson()) };
29         }
30         //如果已經登錄,則跳過驗證
31         return continuation.Invoke();
32     }
33 }
ExecuteAuthorizationFilterAsync核心代碼

2. mvc

  新建Filter 實現 IAuthorizationFilter 接口

 1 public void OnAuthorization(AuthorizationContext filterContext)
 2 {
 3     //throw new NotImplementedException();
 4     if (filterContext.ActionDescriptor.GetCustomAttributes(typeof(AllowAnonymousAttribute), true).Any()) return;
 5 
 6     var token = HttpContext.Current.Request.Cookies["token_manager"]?.Value;
 7 
 8     ActionNeedsPermissions = filterContext.ActionDescriptor.GetCustomAttributes(typeof(PermissionAttribute), false).Cast<PermissionAttribute>().ToList();
 9 
10     if (!token.IsNullOrEmpty())
11     {
12         var user_session = Redis.FindStore<SysUserSession>(token);
13         if (user_session == null)
14         {
15             if (ActionNeedsPermissions.Any(a => a.Kind == PermissionKinds.MENU && a.Action == PermissionActions.AllowAnonymous)) return;
16         }
17         else
18         {
19             if (ActionNeedsPermissions.Count == 0) return;
20             var action_permissions = ActionNeedsPermissions.Select(s => s.Action).ToList();
21             if (user_session.PermissionsArray.Intersect(action_permissions).Count() == action_permissions.Count) return;
22             if (ActionNeedsPermissions.Any(a => a.Action == PermissionActions.AllowAnonymous)) return;
23         }
24     }
25     filterContext.Result = new RedirectResult("/Main/Login");
26 }
OnAuthorization核心代碼

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM