一个简单的ASP.NET MVC下的权限方案
偶然看到博客园里 JadePeng 的 一篇关于ASP.NET MVC下的权限设计的旧文 http://www.cnblogs.com/xiaoqi/archive/2010/01/07/1641570.html,该权限设计中用到5个数据表,利用数据库中的Controller和action记录进行权限判断。
多年前我在一个web form的 web项目里用过类似的方式,当然那里没有controller和action, 主要是将判断函数放在基类里,在页面.cs里调用此函数作为权限判断。
这里提出一个使用XML+数据库+ session(cookie) 的权限方案,旨在简单易懂易用,设计粗劣,大家有兴趣改进。
因为个人觉得在普通的小系统里,角色role是有限的,一个系统里的controller和action总归也是有限的(1K以内?呵呵)。所以我用XML而不是数据库来记录这些。
几点说明:
1、该权限系统是个网站用的,用户简单,因此不涉及到部门这些信息
2、基于将角色与controller、action相关联来判断用户是否有权
3、通过重载AuthorizeAttribute实现
----- 前3项COPY自http://www.cnblogs.com/xiaoqi/archive/2010/01/07/1641570.html
4. 权限的设定保存于XML之中,权限在用户登录后存储于session(理论上cookie也可以),用户登录后判断权限无需查询数据库。
数据库设计:
2个数据表,User 和 UserRole,字段简单,不多做解释了。
相关XML
1. Role.xml --- 记录系统的所有角色,比如
<?xml version="1.0" encoding="utf-8"?> <role> <item> <name>Guest</name> <desciption></desciption> </item> <item> <name>Admin1</name> <desciption></desciption> </item> <item> <name>RegisterUser</name> <desciption>注册用户</desciption> </item> </role>
2. Permission.xml ---- 记录系统所有的操作权限 --- 在MVC里的权限是用Controller和action来判定的,此XML的作用在于提供给UI界面添加、管理系统的所有权限。
节点说明 -- module 节点为controller的名称,一个controller下的action 和 controller本身组成 permission节点
1 <?xml version="1.0" encoding="utf-8"?> 2 <permissionRoot> 3 <module name="Home"> 4 <permission> 5 <controller>Home</controller> 6 <action>Index</action> 7 </permission> 8 <permission> 9 <controller>Home</controller> 10 <action>KeepAlive</action> 11 </permission> 12 <permission> 13 <controller>Home</controller> 14 <action>About</action> 15 </permission> 16 </module> 17 <module name="Account"> 18 <permission> 19 <controller>Account</controller> 20 <action>LogOn</action> 21 </permission> 22 <permission> 23 <controller>Account</controller> 24 <action>LogOff</action> 25 </permission> 26 <permission> 27 <controller>Account</controller> 28 <action>Register</action> 29 </permission> 30 <permission> 31 <controller>Account</controller> 32 <action>Lock</action> 33 </permission> 34 </module> 35 <module name="General"> 36 <permission> 37 <controller>General</controller> 38 <action>SearchResults</action> 39 </permission> 40 </module> 41 <module name="Book"> 42 <permission> 43 <controller>Book</controller> 44 <action>Update</action> 45 </permission> 46 <permission> 47 <controller>Book</controller> 48 <action>Novel</action> 49 </permission> 50 <permission> 51 <controller>Book</controller> 52 <action>History</action> 53 </permission> 54 <permission> 55 <controller>Book</controller> 56 <action>Live</action> 57 </permission> 58 <permission> 59 <controller>Book</controller> 60 <action>Blog</action> 61 </permission> 62 <permission> 63 <controller>Book</controller> 64 <action>Military</action> 65 </permission> 66 <permission> 67 <controller>Book</controller> 68 <action>Other</action> 69 </permission> 70 <permission> 71 <controller>Book</controller> 72 <action>Detail</action> 73 </permission> 74 <permission> 75 <controller>Book</controller> 76 <action>HotBook</action> 77 </permission> 78 <permission> 79 <controller>Book</controller> 80 <action>LatestUpdateBook</action> 81 </permission> 82 <permission> 83 <controller>Book</controller> 84 <action>Index</action> 85 </permission> 86 <permission> 87 <controller>Book</controller> 88 <action>GetData</action> 89 </permission> 90 <permission> 91 <controller>Book</controller> 92 <action>GetBooks</action> 93 </permission> 94 </module> 95 <module name="Books"> 96 <permission> 97 <controller>Books</controller> 98 <action>Management</action> 99 </permission> 100 <permission> 101 <controller>Books</controller> 102 <action>CreateBook</action> 103 </permission> 104 <permission> 105 <controller>Books</controller> 106 <action>Edit</action> 107 </permission> 108 <permission> 109 <controller>Books</controller> 110 <action>Lock</action> 111 </permission> 112 <permission> 113 <controller>Books</controller> 114 <action>Delete</action> 115 </permission> 116 </module> 117 <module name="Role"> 118 <permission> 119 <controller>Role</controller> 120 <action>Management</action> 121 </permission> 122 <permission> 123 <controller>Role</controller> 124 <action>AddRole</action> 125 </permission> 126 <permission> 127 <controller>Role</controller> 128 <action>EditRolePermission</action> 129 </permission> 130 <permission> 131 <controller>Role</controller> 132 <action>Delete</action> 133 </permission> 134 </module> 135 <module name="User"> 136 <permission> 137 <controller>User</controller> 138 <action>Management</action> 139 </permission> 140 <permission> 141 <controller>User</controller> 142 <action>CreateUser</action> 143 </permission> 144 <permission> 145 <controller>User</controller> 146 <action>Edit</action> 147 </permission> 148 <permission> 149 <controller>User</controller> 150 <action>Delete</action> 151 </permission> 152 <permission> 153 <controller>User</controller> 154 <action>AddToRole</action> 155 </permission> 156 <permission> 157 <controller>User</controller> 158 <action>RemoveFromRole</action> 159 </permission> 160 <permission> 161 <controller>User</controller> 162 <action>CheckUserName</action> 163 </permission> 164 </module> 165 <module name="Config"> 166 <permission> 167 <controller>Config</controller> 168 <action>Category</action> 169 </permission> 170 <permission> 171 <controller>Config</controller> 172 <action>Property</action> 173 </permission> 174 <permission> 175 <controller>Config</controller> 176 <action>LinkFrom</action> 177 </permission> 178 <permission> 179 <controller>Config</controller> 180 <action>AddCategoryItem</action> 181 </permission> 182 <permission> 183 <controller>Config</controller> 184 <action>DeleteCategoryItem</action> 185 </permission> 186 <permission> 187 <controller>Config</controller> 188 <action>AddPropertyItem</action> 189 </permission> 190 <permission> 191 <controller>Config</controller> 192 <action>DeletePropertyItem</action> 193 </permission> 194 <permission> 195 <controller>Config</controller> 196 <action>AddLinkFromItem</action> 197 </permission> 198 <permission> 199 <controller>Config</controller> 200 <action>DeleteLinkFromItem</action> 201 </permission> 202 </module> 203 </permissionRoot>
3.RolePermission.xml ---- 记录系统内所有角色的权限记录。结构如下:
<?xml version="1.0" encoding="utf-8"?> <permissionRoot> <role name="Guest"> <permission> <controller>Home</controller> <action>Index</action> </permission> <permission> <controller>Home</controller> <action>KeepAlive</action> </permission> <permission> <controller>Home</controller> <action>About</action> </permission> <permission> <controller>Account</controller> <action>LogOn</action> </permission> <permission> <controller>Account</controller> <action>LogOff</action> </permission> <permission> <controller>Account</controller> <action>Register</action> </permission> <permission> <controller>Account</controller> <action>Lock</action> </permission> <permission> <controller>General</controller> <action>SearchResults</action> </permission> <permission> <controller>Book</controller> <action>Update</action> </permission> <permission> <controller>Book</controller> <action>Novel</action> </permission> <permission> <controller>Book</controller> <action>History</action> </permission> <permission> <controller>Book</controller> <action>Live</action> </permission> <permission> <controller>Book</controller> <action>Blog</action> </permission> <permission> <controller>Book</controller> <action>Other</action> </permission> <permission> <controller>Book</controller> <action>Detail</action> </permission> <permission> <controller>Book</controller> <action>HotBook</action> </permission> <permission> <controller>Book</controller> <action>LatestUpdateBook</action> </permission> <permission> <controller>Book</controller> <action>Index</action> </permission> <permission> <controller>Book</controller> <action>GetData</action> </permission> <permission> <controller>Book</controller> <action>GetBooks</action> </permission> </role> <role name="RegisterUser"> <permission> <controller>Home</controller> <action>Index</action> </permission> <permission> <controller>Home</controller> <action>KeepAlive</action> </permission> <permission> <controller>Home</controller> <action>About</action> </permission> <permission> <controller>Account</controller> <action>LogOn</action> </permission> <permission> <controller>Account</controller> <action>LogOff</action> </permission> <permission> <controller>Account</controller> <action>Register</action> </permission> <permission> <controller>Account</controller> <action>Lock</action> </permission> <permission> <controller>General</controller> <action>SearchResults</action> </permission> <permission> <controller>Book</controller> <action>Update</action> </permission> <permission> <controller>Book</controller> <action>Novel</action> </permission> <permission> <controller>Book</controller> <action>History</action> </permission> <permission> <controller>Book</controller> <action>Live</action> </permission> <permission> <controller>Book</controller> <action>Blog</action> </permission> <permission> <controller>Book</controller> <action>Military</action> </permission> <permission> <controller>Book</controller> <action>Other</action> </permission> <permission> <controller>Book</controller> <action>Detail</action> </permission> <permission> <controller>Book</controller> <action>HotBook</action> </permission> <permission> <controller>Book</controller> <action>LatestUpdateBook</action> </permission> <permission> <controller>Book</controller> <action>Index</action> </permission> <permission> <controller>Book</controller> <action>GetData</action> </permission> <permission> <controller>Book</controller> <action>GetBooks</action> </permission> </role> <role name="Admin1"> <permission> <controller>Home</controller> <action>Index</action> </permission> <permission> <controller>Home</controller> <action>KeepAlive</action> </permission> <permission> <controller>Home</controller> <action>About</action> </permission> <permission> <controller>Account</controller> <action>LogOn</action> </permission> <permission> <controller>Account</controller> <action>LogOff</action> </permission> <permission> <controller>Account</controller> <action>Register</action> </permission> <permission> <controller>Account</controller> <action>Lock</action> </permission> <permission> <controller>General</controller> <action>SearchResults</action> </permission> <permission> <controller>Book</controller> <action>Update</action> </permission> <permission> <controller>Book</controller> <action>Novel</action> </permission> <permission> <controller>Book</controller> <action>History</action> </permission> <permission> <controller>Book</controller> <action>Live</action> </permission> <permission> <controller>Book</controller> <action>Blog</action> </permission> <permission> <controller>Book</controller> <action>Military</action> </permission> <permission> <controller>Book</controller> <action>Other</action> </permission> <permission> <controller>Book</controller> <action>Detail</action> </permission> <permission> <controller>Book</controller> <action>HotBook</action> </permission> <permission> <controller>Book</controller> <action>LatestUpdateBook</action> </permission> <permission> <controller>Book</controller> <action>Index</action> </permission> <permission> <controller>Book</controller> <action>GetData</action> </permission> <permission> <controller>Book</controller> <action>GetBooks</action> </permission> <permission> <controller>Books</controller> <action>Management</action> </permission> <permission> <controller>Books</controller> <action>CreateBook</action> </permission> <permission> <controller>Books</controller> <action>Edit</action> </permission> <permission> <controller>Books</controller> <action>Lock</action> </permission> <permission> <controller>Books</controller> <action>Delete</action> </permission> <permission> <controller>Role</controller> <action>Management</action> </permission> <permission> <controller>Role</controller> <action>AddRole</action> </permission> <permission> <controller>Role</controller> <action>EditRolePermission</action> </permission> <permission> <controller>Role</controller> <action>Delete</action> </permission> <permission> <controller>User</controller> <action>Management</action> </permission> <permission> <controller>User</controller> <action>CreateUser</action> </permission> <permission> <controller>User</controller> <action>Edit</action> </permission> <permission> <controller>User</controller> <action>Delete</action> </permission> <permission> <controller>User</controller> <action>AddToRole</action> </permission> <permission> <controller>User</controller> <action>RemoveFromRole</action> </permission> <permission> <controller>User</controller> <action>CheckUserName</action> </permission> <permission> <controller>Config</controller> <action>Category</action> </permission> <permission> <controller>Config</controller> <action>Property</action> </permission> <permission> <controller>Config</controller> <action>LinkFrom</action> </permission> <permission> <controller>Config</controller> <action>AddCategoryItem</action> </permission> <permission> <controller>Config</controller> <action>DeleteCategoryItem</action> </permission> <permission> <controller>Config</controller> <action>AddPropertyItem</action> </permission> <permission> <controller>Config</controller> <action>DeletePropertyItem</action> </permission> <permission> <controller>Config</controller> <action>AddLinkFromItem</action> </permission> <permission> <controller>Config</controller> <action>DeleteLinkFromItem</action> </permission> </role> </permissionRoot>
流程
流程较为简单,故文字说明。
1.User表用户登录的同时,从UserRole表中查询出用户所拥有的角色,然后在RolePermission.xml里查询得到所拥有角色的所有权限permission列表,每个permission格式为 controller_action,组成一个list,踢出重复项,再转为string,保存于session之中。session 同时保存用户名
2.新建一个filter: UserAuthorizeAttribute,继承AuthorizeAttribute, 重写OnAuthorization。在此方法体内 做判断,如果用户的permission列表里包含当前的controller,action,验证通过,否则反之。
3.对于未登陆用户,默认采用Guest角色的权限。(视系统需要而定)
主要代码
1.UserAuthorizeAttribute.cs
1 namespace Clover.Net.Web.Code.Filters 2 { 3 /// <SUMMARY> 4 /// 自定义AuthorizeAttribute 5 /// </SUMMARY> 6 public class UserAuthorizeAttribute : AuthorizeAttribute 7 { 8 private PermissionRepository _permissionRepository = new PermissionRepository(); 9 10 public override void OnAuthorization(AuthorizationContext filterContext) 11 { 12 string rolePermissionXml = filterContext.HttpContext.Server.MapPath("/Config/RolePermission.xml"); 13 var user = filterContext.HttpContext.Session["user_Name"]; 14 var permissions = filterContext.HttpContext.Session["user_Permissions"]; 15 16 List<string> user_Permissions = new List<string>(); 17 18 if (user != null || permissions != null) 19 { 20 string[] userTotalPermissions = permissions.ToString().Split(','); 21 foreach (var per in userTotalPermissions) 22 { 23 user_Permissions.Add(per); 24 } 25 } 26 //用户为空,赋予Guest and set up the session 27 else 28 { 29 string guestTotalPermissions = string.Empty; 30 List<PermissionItem> permissionList = _permissionRepository.CurrentRolePermissions(rolePermissionXml, "Guest"); 31 foreach (var per in permissionList) 32 { 33 guestTotalPermissions = guestTotalPermissions + "," + per.Controller + "_" + per.Action; 34 } 35 filterContext.HttpContext.Session["user_Name"] = "Guest"; 36 filterContext.HttpContext.Session["user_Permissions"] = guestTotalPermissions; 37 38 string[] userTotalPermissions = guestTotalPermissions.ToString().Split(','); 39 foreach (var per in userTotalPermissions) 40 { 41 user_Permissions.Add(per); 42 } 43 } 44 45 string controller = filterContext.RouteData.Values["controller"].ToString(); 46 string action = filterContext.RouteData.Values["action"].ToString(); 47 48 string id = String.Empty; 49 if (filterContext.RouteData.Values["id"] != null) 50 { 51 id = filterContext.RouteData.Values["id"].ToString(); 52 } 53 var isAllowed = this.IsAllowed(controller, action, user_Permissions); 54 55 if (!isAllowed) 56 { 57 //filterContext.RequestContext.HttpContext.Response.Write("无权访问"); 58 //filterContext.RequestContext.HttpContext.Response.End(); 59 60 //filterContext.RequestContext.HttpContext.Response.Redirect("../Account/LogOn"); 61 62 string returnUrl = String.Empty; 63 if (String.IsNullOrEmpty(id)) 64 { 65 returnUrl = "/" + controller + "/" + action; 66 } 67 else 68 { 69 returnUrl = "/" + controller + "/" + action + "/" + id; 70 } 71 72 filterContext.Controller.ViewData["ReturnUrl"] = returnUrl; 73 74 if (filterContext.HttpContext.Session["user_Name"].ToString() == "Guest") 75 { 76 filterContext.Controller.ViewData["Title"] = "请登录"; 77 filterContext.Controller.ViewData["Description"] = "您还没有登录,请登录!"; 78 //filterContext.RequestContext.HttpContext.Response.Redirect("~/Account/LogOn?ReturnUrl=" + returnUrl); 79 } 80 else 81 { 82 filterContext.Controller.ViewData["Title"] = "权限问题"; 83 filterContext.Controller.ViewData["Description"] = "对不起,你无权访问此页面,请联系管理员!"; 84 } 85 86 filterContext.Result = new ViewResult() 87 { 88 ViewName = "Error", 89 ViewData = filterContext.Controller.ViewData, 90 }; 91 } 92 93 } 94 95 /// <SUMMARY> 96 /// 判断是否允许访问 97 /// </SUMMARY> 98 /// <SPAN name="user"> </SPAN>用户 99 /// <SPAN name="controller"> </SPAN>控制器 100 /// <SPAN name="action"> </SPAN>action 101 /// <RETURNS>是否允许访问</RETURNS> 102 public bool IsAllowed(string controller, string action, List<string> user_Permissions) 103 { 104 string permission = controller + "_" + action; 105 106 //如果是登陆,默认通过 107 if (permission == "Account_LogOn" || permission == "Account_Logon" || permission == "Account_LogOff" || permission == "Account_Register" || permission == "User_CheckUserName") 108 { 109 return true; 110 } 111 112 if (user_Permissions.Contains(permission)) 113 { 114 return true; 115 } 116 // 默认禁止访问 117 return false; 118 } 119 } 120 }
2.SetUpUser函数,在用户登录验证通过之后调用,作用是将用户名和用户权限列表保存于session之中
public void SetUpUser(string userName) { string userTotalPermissions = string.Empty; List<string> userTotalPermissionsList = new List<string>(); List<string> userRoles = this.GetUserRoles(userName); foreach (var role in userRoles) { List<PermissionItem> permissionList = _IPermissionRepository.CurrentRolePermissions(rolePermissionXml, role); if (permissionList != null) { foreach (var per in permissionList) { string permissionText = per.Controller + "_" + per.Action; userTotalPermissionsList.Add(permissionText); } } } List<string> userPermissionsList = userTotalPermissionsList.Distinct().ToList(); foreach (var per in userPermissionsList) { userTotalPermissions = userTotalPermissions + "," + per; } Session["user_Name"] = userName; Session["user_Permissions"] = userTotalPermissions.ToString(); }
具体使用
只需要在 BaseController 顶部添加一个 [UserAuthorize]
-------------------------------------------------------------------
测试界面
纯属给大家看下结果,呵呵,没有权限被拒绝
后记
本方案较为简单,适合小项目用哈。当前用户的权限列表存储于session中,再次判断权限时候无需再次读取数据库。
还有很多问题和不足之处,欢迎大家指正。