微软在推出mvc框架不久,短短几年里,版本更新之快,真是大快人心,微软在这种优秀的框架上做了大量的精力投入,是值得赞同的,毕竟程序员驾驭在这种框架上,能够强力的精化代码,代码层次也更加优雅,扩展较为方便,使之程序员把更多的精力投入到业务中来。

很多时候我就在想,是不是该把传统的用户权限管理换个方式了呢?换成MVC AOP的思想权限控制,诸如controller action这种方案行的通吗?答案是肯定的,人的思想永远是第一位!

看看我们想要达到的效果

1)权限列表

2)菜单权限列表

3)角色权限列表

4)用户权限列表

5)主从菜单的配置

6)图标的自由定制

1.基于controller action控制权限的好处

其实合起来看,controller与action即控制了一个页面的行为,如能是否查看,写入,修改权限,而我们在开发的过程中,这些方法都已完成,这样省去了像传统方式对每个页面重新控制生成的步骤。当前通过反射程序集收集所有的controller action信息,自动化收集权限控制是比较可观的。

2.基于controller action控制权限的方案

权限列表通过反射自动从程序集获取,管理员(默认有最高权限)把权限分配给用户及分配权限分配给角色,管理员对用户分配权限角色权限,如图所示

用户权限列表及角色权限列表分配成形Controller与Action信息后,通过代码控制对应的控制器及方法是否有权限。

3.基于controller action控制权限信息的提取的方案

有些方法不需要提取的,有些方法需要权限控制,为了让程序方便提取权限信息,我们加入特性,如果方法或控制器有此特性,即要控制的,当然为了节约代码,默认特性是false,这样,没有加特性的或者特性是false的,都不用提取!

4.基于mvc controller和action 权限管理流程图

不明白的亲们不用太着急,下面开始详细的步骤吧!

首先我们通过上面的分析,我们用模型来一点一点的剖析

从左至右,相关的模型是,权限信息列表,角色列表,用户信息列表,部门列表,菜单列表

1)一个用户可以有多个权限,一个权限可以分配多个用户,所以是多对多的关系

2)一个角色可以有多个权限,一个权限可以分配多个角色,所以是多对多的关系

3)一个用户可以有多个角色,一个角色可以分配多个用户,所以是多对多的关系

4)一个用户可以有多个部门,一个部门可以分配多个角户,所以是多对多的关系

5)菜单列表,主要针对后台每个用户或角色不同的展示方式,以及可以自定义图片等

好了,到此为止,我们开始正式的工作。

由上列模型,我们采用code first生成数据库,如何使用 code first 请搜索下百度或将来有专门的章节介绍,这里不再累赘说明!

我们添加,添加以后几个model类

1)权限控制类:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.ComponentModel;
  6. using System.ComponentModel.DataAnnotations;
  7.  
  8. namespace WL.Models.Permission
  9. {
  10. [DisplayName("控制权限")]
  11. public class ActionPermission
  12. {
  13. [Key]
  14. [DisplayName("控制权限ID")]
  15. public int ActionPermissonID { set; get; }
  16.  
  17. [DisplayName("控制权限名称")]
  18. public string ActionPermissionName { set; get; }
  19.  
  20. [DisplayName("控制器权限名称")]
  21. public string ControllerPermissionName { set; get; }
  22.  
  23. [DisplayName("说明")]
  24. public string Description { set; get; }
  25.  
  26. [DisplayName("创建时间")]
  27. public DateTime CreateDate { set; get; }
  28.  
  29. [DisplayName("操作用户名")]
  30. public string Operator { set; get; }
  31.  
  32. [DisplayName("最后修改时间")]
  33. public DateTime LateDate { set; get; }
  34.  
  35. [DisplayName("图标")]
  36. public string Icon { get; set; }
  37.  
  38. [DisplayName("状态")]
  39. public int State { set; get; }
  40.  
  41. [Description("用户实体集合")]
  42. public virtual ICollection<User> UserCollection { get; set; }
  43.  
  44. [Description("角色实体")]
  45. public virtual ICollection<Role> RoleCollection { get; set; }
  46.  
  47. }
  48. }

2)部门类:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.ComponentModel;
  6. using System.ComponentModel.DataAnnotations;
  7.  
  8. namespace WL.Models.Permission
  9. {
  10. [DisplayName("部门")]
  11. [DisplayColumn("DepartMentID")]
  12. public class DepartMent
  13. {
  14. [Key]
  15. [DisplayName("部门ID")]
  16. public int DepartMentID { set; get; }
  17.  
  18. [DisplayName("部门名称")]
  19. public string DepartName { set; get; }
  20.  
  21. [DisplayName("说明")]
  22. public string Description { set; get; }
  23.  
  24. [DisplayName("创建时间")]
  25. public DateTime CreateDate { set; get; }
  26.  
  27. [DisplayName("操作用户名")]
  28. public string Operator { set; get; }
  29.  
  30. [DisplayName("最后修改时间")]
  31. public DateTime LateDate { set; get; }
  32.  
  33. [DisplayName("图标")]
  34. public string Icon { get; set; }
  35.  
  36. [DisplayName("状态")]
  37. public int State { set; get; }
  38.  
  39. [Description("用户实体集合")]
  40. public virtual ICollection<User> UserCollection { get; set; }
  41.  
  42. }
  43. }

3)菜单类:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.ComponentModel;
  6. using System.ComponentModel.DataAnnotations;
  7.  
  8. namespace WL.Models.Permission
  9. {
  10. [DisplayName("菜单")]
  11. [DisplayColumn("MenuID")]
  12. public class Menu
  13. {
  14. [Key]
  15. [DisplayName("菜单ID")]
  16. public int MenuID { set; get; }
  17.  
  18. [DisplayName("ParentID")]
  19. public int ParentsID { set; get; }
  20.  
  21. [DisplayName("菜单名称")]
  22. public string MenuName { set; get; }
  23.  
  24. [DisplayName("菜单图标")]
  25. public string MenuIco { set; get; }
  26.  
  27. [DisplayName("说明")]
  28. public string Descriptotion { set; get; }
  29.  
  30. [DisplayName("控制权限名称")]
  31. public string ActionPermissionName { set; get; }
  32.  
  33. [DisplayName("控制器权限名称")]
  34. public string ControllerPermissionName { set; get; }
  35.  
  36. [DisplayName("链接地址")]
  37. public string Url { set; get; }
  38.  
  39. [DisplayName("排序")]
  40. public string Sort { set; get; }
  41.  
  42. [DisplayName("创建时间")]
  43. public DateTime CreateDate { set; get; }
  44.  
  45. [DisplayName("操作用户名")]
  46. public string Operator { set; get; }
  47.  
  48. [DisplayName("最后修改时间")]
  49. public DateTime LateDate { set; get; }
  50.  
  51. [DisplayName("图标")]
  52. public string Icon { get; set; }
  53.  
  54. [DisplayName("状态")]
  55. public int State { set; get; }
  56.  
  57. }
  58. }

4)角色类

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.ComponentModel.DataAnnotations;
  6. using System.ComponentModel;
  7.  
  8. namespace WL.Models.Permission
  9. {
  10. [DisplayName("角色成员")]
  11. [DisplayColumn("RoleID")]
  12. public class Role
  13. {
  14. [Key]
  15. [DisplayName("用户ID")]
  16. public int RoleID { set; get; }
  17.  
  18. [DisplayName("角色名")]
  19. public string RoleName { set; get; }
  20.  
  21. [DisplayName("说明")]
  22. public string Description { set; get; }
  23.  
  24. [DisplayName("创建时间")]
  25. public DateTime CreateDate { set; get; }
  26.  
  27. [DisplayName("操作用户名")]
  28. public string Operator { set; get; }
  29.  
  30. [DisplayName("最后修改时间")]
  31. public DateTime LateDate { set; get; }
  32.  
  33. [DisplayName("图标")]
  34. public string Icon { get; set; }
  35.  
  36. [DisplayName("状态")]
  37. public int State { set; get; }
  38.  
  39. [Description("用户实体集合")]
  40. public virtual ICollection<User> UserCollection { get; set; }
  41.  
  42. [Description("控制权限实体集合")]
  43. public virtual ICollection<ActionPermission> ActionPermissionCollection { get; set; }
  44.  
  45. }
  46. }

5)用户后台管理员类

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.ComponentModel.DataAnnotations;
  6. using System.ComponentModel;
  7.  
  8. namespace WL.Models.Permission
  9. {
  10. [DisplayName("用户成员")]
  11. [DisplayColumn("UserId")]
  12. public class User
  13. {
  14. [Key]
  15. [DisplayName("用户ID")]
  16. public int UserID { set; get; }
  17.  
  18. [DisplayName("用户名称")]
  19. public string UserName { set; get; }
  20.  
  21. [DisplayName("用户密码")]
  22. public string PassWord { set; get; }
  23.  
  24. [DisplayName("用户邮件")]
  25. public string Mail { set; get; }
  26.  
  27. [DisplayName("用户电话")]
  28. public string Phone { set; get; }
  29.  
  30. [DisplayName("说明")]
  31. public string Description { set; get; }
  32.  
  33. [DisplayName("创建时间")]
  34. public DateTime CreateDate { set; get; }
  35.  
  36. [DisplayName("操作用户名")]
  37. public string Operator { set; get; }
  38.  
  39. [DisplayName("最后修改时间")]
  40. public DateTime LateDate { set; get; }
  41.  
  42. [DisplayName("图标")]
  43. public string Icon { get; set; }
  44.  
  45. [DisplayName("状态")]
  46. public int State { set; get; }
  47.  
  48. [Description("部门实体")]
  49. public virtual ICollection<DepartMent> DepartMentCollection { get; set; }
  50.  
  51. [Description("角色实体")]
  52. public virtual ICollection<Role> RoleCollection { get; set; }
  53.  
  54. [Description("控制权限实体集合")]
  55. public virtual ICollection<ActionPermission> ActionPermissionCollection { get; set; }
  56.  
  57. }
  58. }

在项目中任意添加一个项目 MvsApp->PermissionContext

并配置web.config

在此项目中设为启始项目 然后台下操作

设置默认项目

在命令行里输入

Enable-Migrations-->Add-Migration Rating-->update-database

在数据中将会生成权限数据库

我们采用code first好处在于,“EF”自动为我们创建中间关系表,省去了中间我们手工创建的省麻烦。如果不清楚的朋友,请查EF Code first 自动迁移相关文章。

然后,我们布局各个控制器如下图

可以看出,任何一个权限控制器类都所继续一个基类baseController,这样写的好处只有一个,节约代码,因为‘懒’。

在基baseControlle类中我们将为权限控制进行描述。

基于AOP的思想,在此基类baseControlle中,重写 Controller的 OnActionExecuting()方法,来判断用户的相关权限,大致逻缉流程如下

通过上面的流程的分析,代码如下

1)判断用否是否成功登录

  1. #region -----校验用户是否登录进入网站的-----
  2. base.OnActionExecuting(filterContext);
  3.  
  4. HttpCookie UserInfo = System.Web.HttpContext.Current.Request.Cookies.Get("COOKIE_NAME_FOR_USER");
  5.  
  6. //检验用户是否已经登录,如果登录则不执行,否则则执行下面的跳转代码
  7. if (UserInfo == null)
  8. {
  9. filterContext.Result = RedirectToRoute(new { Controller = "Login", Action = "Index" });
  10. return;
  11. }
  12. #endregion
  13.  
  14. var UserInfoStr = UserInfo["COOKIE_NAME_FOR_USER_INFO"].ToString();
  15.  
  16. if (string.IsNullOrEmpty(UserInfoStr))
  17. {
  18. filterContext.Result = RedirectToRoute(new { Controller = "Login", Action = "Index" });
  19. return;
  20. }

2)提取登录用户相关登录的信息(注册这里是后台相关演示,安全性没做处理)

  1. var userNameArray = UserInfoStr.Split(new string[] { "$|$" }, StringSplitOptions.RemoveEmptyEntries);
  2. var username = userNameArray[0];
  3. var pwdword = userNameArray[1];
  4.  
  5. CurrentUserInfo = this.userInfoService.LoadEntites(u => username.Equals(u.UserName) && pwdword.ToLower().Equals(u.PassWord.ToLower()) && u.State == 0).FirstOrDefault();
  6.  
  7. if (CurrentUserInfo == null)
  8. {
  9. filterContext.Result = RedirectToRoute(new { Controller = "Login", Action = "Index" });
  10. return;
  11. }

3)获取当前用户所在的控制器与方法并根据权限特性(上文提到的自定义判断权限依据)判断当前控制器是否需要权限控制

  1. //先将当前的请求,到权限表里面去找对应的数据
  2. System.Web.Routing.RouteData Rotedate = filterContext.RequestContext.RouteData;
  3. string controller = (RouteData.Values["controller"] ?? "").ToString().ToLower();
  4. string action = (RouteData.Values["action"] ?? "").ToString().ToLower();
  5.  
  6. //默认
  7. if (HasDefaultAction(filterContext))
  8. return;
  1. private bool HasDefaultAction(ActionExecutingContext filterContext)
  2. {
  3. if (CurrentUserInfo.UserName == "admin")
  4. return true;
  5.  
  6. Type t = filterContext.ActionDescriptor.ControllerDescriptor.ControllerType;
  7. string actionname = filterContext.RouteData.Values["action"].ToString();
  8. //默认没用应用权限特性,仅不需要权限的才进行控件该特性,如果应用,HasPermission必须false才可以进行放行些权限
  9. object[] astri = GetPermissionAttribute<PermissionAttribute>(actionname, t);
  10. if (astri.Length > 0)
  11. {
  12. //更具自定义的特性得到需要调用的类名与方法名
  13. PermissionAttribute u = astri[0] as PermissionAttribute;
  14. return !u.RequiredPermission;
  15. }
  16. return false;
  17. }

4)至此用户程序都通过后,开始用户相关的权限判断。有两条线路判断,一是角户权限判断,二是用户权限判断。

角户权限判断

  1. //然后和权限表进行对比,如果取出来则通过请求,否则不通过
  2. //取出当前权限的数据
  3. //想去用户权限表里面查询有没有数据
  4. //分析线路 User->Role->Action
  5. //拿到当前的用户信息
  6. var userCurrent = userInfoService.LoadEntites(u => u.UserID == CurrentUserInfo.UserID).FirstOrDefault();
  7. var role = (from r in userCurrent.RoleCollection
  8. from c in r.ActionPermissionCollection
  9. where c.ActionPermissionName.ToLower() == action && c.ControllerPermissionName.ToLower() == controller && c.State == 0
  10. select r).FirstOrDefault();
  11. if (role != null)
  12. return;

用户权限(后台用户)判断

  1. //分析线路 User->Action
  2. var user = (from c in userCurrent.ActionPermissionCollection
  3. where c.ActionPermissionName.ToLower() == action && c.ControllerPermissionName.ToLower() == controller && c.State == 0
  4. select c).FirstOrDefault();
  5.  
  6. if (user != null)
  7. return;

其它可能的错误

  1. public void EndRequest(ActionExecutingContext filterContext)
  2. {
  3. filterContext.Result = RedirectToRoute(new { Controller = "Home", Action = "Error" });
  4. }

至此,一个基本的用户控制已经完成

完整的代码:

  1. #region -----校验用户是否登录进入网站的-----
  2. base.OnActionExecuting(filterContext);
  3.  
  4. HttpCookie UserInfo = System.Web.HttpContext.Current.Request.Cookies.Get("COOKIE_NAME_FOR_USER");
  5.  
  6. //检验用户是否已经登录,如果登录则不执行,否则则执行下面的跳转代码
  7. if (UserInfo == null)
  8. {
  9. filterContext.Result = RedirectToRoute(new { Controller = "Login", Action = "Index" });
  10. return;
  11. }
  12. #endregion
  13.  
  14. var UserInfoStr = UserInfo["COOKIE_NAME_FOR_USER_INFO"].ToString();
  15.  
  16. if (string.IsNullOrEmpty(UserInfoStr))
  17. {
  18. filterContext.Result = RedirectToRoute(new { Controller = "Login", Action = "Index" });
  19. return;
  20. }
  21.  
  22. var userNameArray = UserInfoStr.Split(new string[] { "$|$" }, StringSplitOptions.RemoveEmptyEntries);
  23. var username = userNameArray[0];
  24. var pwdword = userNameArray[1];
  25.  
  26. CurrentUserInfo = this.userInfoService.LoadEntites(u => username.Equals(u.UserName) && pwdword.ToLower().Equals(u.PassWord.ToLower()) && u.State == 0).FirstOrDefault();
  27.  
  28. if (CurrentUserInfo == null)
  29. {
  30. filterContext.Result = RedirectToRoute(new { Controller = "Login", Action = "Index" });
  31. return;
  32. }
  33.  
  34. #region -------检验用户是否有访问此地址的权利----
  35. //先将当前的请求,到权限表里面去找对应的数据
  36. System.Web.Routing.RouteData Rotedate = filterContext.RequestContext.RouteData;
  37. string controller = (RouteData.Values["controller"] ?? "").ToString().ToLower();
  38. string action = (RouteData.Values["action"] ?? "").ToString().ToLower();
  39.  
  40. //默认
  41. if (HasDefaultAction(filterContext))
  42. return;
  43.  
  44. //然后和权限表进行对比,如果取出来则通过请求,否则不通过
  45. //取出当前权限的数据
  46. //想去用户权限表里面查询有没有数据
  47. //分析线路 User->Role->Action
  48. //拿到当前的用户信息
  49. var userCurrent = userInfoService.LoadEntites(u => u.UserID == CurrentUserInfo.UserID).FirstOrDefault();
  50. var role = (from r in userCurrent.RoleCollection
  51. from c in r.ActionPermissionCollection
  52. where c.ActionPermissionName.ToLower() == action && c.ControllerPermissionName.ToLower() == controller && c.State == 0
  53. select r).FirstOrDefault();
  54. if (role != null)
  55. return;
  56.  
  57. //分析线路 User->Action
  58. var user = (from c in userCurrent.ActionPermissionCollection
  59. where c.ActionPermissionName.ToLower() == action && c.ControllerPermissionName.ToLower() == controller && c.State == 0
  60. select c).FirstOrDefault();
  61.  
  62. if (user != null)
  63. return;
  64.  
  65. EndRequest(filterContext);

到此,可以看到任何一个控制器都将受到这个基控制器的‘影响’,为此我们没必要去一个一个的控制器类实现,AOP的思想,确实为我们节约了不少代码。

简单的总结下思路:

用户登录后写入cookie ,在此基类中,读取此cookie存储的相关用户相关信息,如用户名等,同时读取当前访问的mvc相关的controller action用户和角色相关的权限信息,

如果用户有此controller action权利,那么我们即可放行

为了节约数据库的资源,在此基础加入判断,当前访问的controller action 与用户当前操作的controller action 是否控制特性判断,如果在些方法上不存在权限控制的特性或者为false,那么,说明不需要权限控制,直接放行。

(转载自原作者:谷歌's谷歌's博客园
出处:http://www.cnblogs.com/laogu2/ )

 

尝试asp.net mvc 基于controller action 方式权限控制方案可行性(转载)的更多相关文章

  1. 尝试asp.net mvc 基于controller action 方式权限控制方案可行性

    微软在推出mvc框架不久,短短几年里,版本更新之快,真是大快人心,微软在这种优秀的框架上做了大量的精力投入,是值得赞同的,毕竟程序员驾驭在这种框架上,能够强力的精化代码,代码层次也更加优雅,扩展较为方 ...

  2. 返璞归真 asp.net mvc (3) - Controller/Action

    原文:返璞归真 asp.net mvc (3) - Controller/Action [索引页] [源码下载] 返璞归真 asp.net mvc (3) - Controller/Action 作者 ...

  3. ASP.NET MVC中controller和view相互传值的方式

    ASP.NET MVC中Controller向view传值的方式: ViewBag.ViewData.TempData 单个值的传递 Json 匿名类型 ExpandoObject Cookie Vi ...

  4. ASP.NET MVC 基于角色的权限控制系统的示例教程

    上一次在 .NET MVC 用户权限管理示例教程中讲解了ASP.NET MVC 通过AuthorizeAttribute类的OnAuthorization方法讲解了粗粒度控制权限的方法,接下来讲解基于 ...

  5. ASP.NET MVC基于标注特性的Model验证:将ValidationAttribute应用到参数上

    原文:ASP.NET MVC基于标注特性的Model验证:将ValidationAttribute应用到参数上 ASP.NET MVC默认采用基于标准特性的Model验证机制,但是只有应用在Model ...

  6. ASP.NET MVC基于标注特性的Model验证:一个Model,多种验证规则

    原文:ASP.NET MVC基于标注特性的Model验证:一个Model,多种验证规则 对于Model验证,理想的设计应该是场景驱动的,而不是Model(类型)驱动的,也就是对于同一个Model对象, ...

  7. 【部分】ASP.NET MVC的Controller接收输入详解

    原文:https://blog.csdn.net/lxrj2008/article/details/79455360 ASP.NET mvc的Controller要正确的响应用户发出的请求就要获取到用 ...

  8. Asp.net MVC 中Controller返回值类型ActionResult

    [Asp.net MVC中Controller返回值类型] 在mvc中所有的controller类都必须使用"Controller"后缀来命名并且对Action也有一定的要求: 必 ...

  9. 在ASP.NET MVC中以post方式传递数组参数的示例

    最近在工作中用到了在ASP.NET MVC中以post方式传递数组参数的情况,记录下来,以供参考. 一.准备参数对象 在本例中,我会传递两个数组参数:一个字符串数组,一个自定义对象数组.这个自定义对象 ...

随机推荐

  1. Partition Equal Subset Sum

    Given a non-empty array containing only positive integers, find if the array can be partitioned into ...

  2. 在Eclipse中运行JAVA代码远程操作HBase的示例

    在Eclipse中运行JAVA代码远程操作HBase的示例 分类: 大数据 2014-03-04 13:47 3762人阅读 评论(2) 收藏 举报 下面是一个在Windows的Eclipse中通过J ...

  3. 哈希与字典树与KMP

    hash讲解 主要记录hash的公式: ; i<=len; i++) { Hash[i]=(Hash[i-]*)%mod)%mod; } 求hash的公式是这个,怎么求一小段的hash值呢? ; ...

  4. scope属性

    scope属性 1.<bean>属性 2.作用:控制对象有效范围(单例.多例等) 3.<bean/> 标签对应的对象默认是单例的 3.1 无论获取多少次,都是同一个对象 4.s ...

  5. mysql 表锁进程非常多的情况

    今天要说的是mysql 的 MYISAM引擎下的表锁问题. 通常来说,在MyISAM里读写操作是串行的,但当对同一个表进行查询和插入操作时,为了降低锁竞争的频率,根据concurrent_insert ...

  6. php 制作验证码不显示的问题

    php制作验证码的代码,这里就不多说了,网上有很多的,这里说一些可能遇到的问题. 1. 首先是检查自己的php.ini文件,是否支持gd库. 2.保证代码没有出问题. 3.检查字体文件路径是否正确. ...

  7. 2019.01.17 bzoj2333: [SCOI2011]棘手的操作(启发式合并)

    传送门 启发式合并菜题. 题意:支持与连通块有关的几种操作. 要求支持连边,单点修改,连通块修改,全局修改和单点查值,连通块查最大值和全局最大值. 我们对每个连通块和答案用可删堆维护最大值,然后用启发 ...

  8. Docker技术入门与实战(文摘)

    第一部分 基础入门 第1章 初识容器与Docker 第2章 核心概念与安装配置 第二部分 实战案例 第三部分 进阶技能 第四部分 开源项目

  9. error C4430: error 2141

    c:\evan\workspace\1\1\netwowkippack.h(50) : error C2146: 语法错误 : 缺少“;”(在标识符“nSourPort”的前面) c:\evan\wo ...

  10. windows10; ERROR 1010 (HY000): Error dropping database (can't rmdir './test/', errno: 17);默认数据库位置查找

    1.想要导入数据到一个数据库中,但是,无法导入,同时也无法删除数据库重新建立-----------------------------备份当前数据库 2,分析:很多资料显示说数据库下有异常文件,于是就 ...