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

一. 權限場景分析:

1. 系統具備角色概念, 部門概念, 且都具備相應不一樣的權限web

2. 用戶具備多個角色, 多個部門等關係, 而且能給單個用戶指派獨有的權限數據庫

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

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

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

 

 

二. 數據庫表設計

  1. 角色表mvc

  2. 部門表ide

  3. 用戶表spa

  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核心代碼
相關文章
相關標籤/搜索