• 尝试asp.net mvc 基于controller action 方式权限控制方案可行性(转载)


    微软在推出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)权限控制类:

    复制代码
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ComponentModel;
    using System.ComponentModel.DataAnnotations;
    
    namespace WL.Models.Permission
    {
        [DisplayName("控制权限")]
        public class ActionPermission
        {
            [Key]
            [DisplayName("控制权限ID")]
            public int ActionPermissonID { set; get; }
    
            [DisplayName("控制权限名称")]
            public string ActionPermissionName { set; get; }
    
            [DisplayName("控制器权限名称")]
            public string ControllerPermissionName { set; get; }
    
            [DisplayName("说明")]
            public string Description { set; get; }
    
            [DisplayName("创建时间")]
            public DateTime CreateDate { set; get; }
    
            [DisplayName("操作用户名")]
            public string Operator { set; get; }
    
            [DisplayName("最后修改时间")]
            public DateTime LateDate { set; get; }
    
            [DisplayName("图标")]
            public string Icon { get; set; }
    
            [DisplayName("状态")]
            public int State { set; get; }
    
            [Description("用户实体集合")]
            public virtual ICollection<User> UserCollection { get; set; }
    
            [Description("角色实体")]
            public virtual ICollection<Role> RoleCollection { get; set; }
    
        }
    }
    复制代码

      2)部门类:

    复制代码
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ComponentModel;
    using System.ComponentModel.DataAnnotations;
    
    namespace WL.Models.Permission
    {
        [DisplayName("部门")]
        [DisplayColumn("DepartMentID")]
        public class DepartMent
        {
            [Key]
            [DisplayName("部门ID")]
            public int DepartMentID { set; get; }
    
            [DisplayName("部门名称")]
            public string DepartName { set; get; }
    
            [DisplayName("说明")]
            public string Description { set; get; }
    
            [DisplayName("创建时间")]
            public DateTime CreateDate { set; get; }
    
            [DisplayName("操作用户名")]
            public string Operator { set; get; }
    
            [DisplayName("最后修改时间")]
            public DateTime LateDate { set; get; }
    
            [DisplayName("图标")]
            public string Icon { get; set; }
    
    
            [DisplayName("状态")]
            public int State { set; get; }
    
            [Description("用户实体集合")]
            public virtual ICollection<User> UserCollection { get; set; }
    
        }
    }
    复制代码

       3)菜单类:

    复制代码
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ComponentModel;
    using System.ComponentModel.DataAnnotations;
    
    namespace WL.Models.Permission
    {
        [DisplayName("菜单")]
        [DisplayColumn("MenuID")]
        public class Menu
        {
            [Key]
            [DisplayName("菜单ID")]
            public int MenuID { set; get; }
    
            [DisplayName("ParentID")]
            public int ParentsID { set; get; }
    
            [DisplayName("菜单名称")]
            public string MenuName { set; get; }
    
            [DisplayName("菜单图标")]
            public string MenuIco { set; get; }
    
            [DisplayName("说明")]
            public string Descriptotion { set; get; }
    
    
            [DisplayName("控制权限名称")]
            public string ActionPermissionName { set; get; }
    
            [DisplayName("控制器权限名称")]
            public string ControllerPermissionName { set; get; }
    
            [DisplayName("链接地址")]
            public string Url { set; get; }
    
            [DisplayName("排序")]
            public string Sort { set; get; }
    
            [DisplayName("创建时间")]
            public DateTime CreateDate { set; get; }
    
            [DisplayName("操作用户名")]
            public string Operator { set; get; }
    
            [DisplayName("最后修改时间")]
            public DateTime LateDate { set; get; }
    
            [DisplayName("图标")]
            public string Icon { get; set; }
    
    
            [DisplayName("状态")]
            public int State { set; get; }
    
        }
    }
    复制代码

    4)角色类

    复制代码
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel;
    
    namespace WL.Models.Permission
    {
        [DisplayName("角色成员")]
        [DisplayColumn("RoleID")]
        public class Role
        {
            [Key]
            [DisplayName("用户ID")]
            public int RoleID { set; get; }
    
            [DisplayName("角色名")]
            public string RoleName { set; get; }
    
            [DisplayName("说明")]
            public string Description { set; get; }
    
            [DisplayName("创建时间")]
            public DateTime CreateDate { set; get; }
    
            [DisplayName("操作用户名")]
            public string Operator { set; get; }
    
            [DisplayName("最后修改时间")]
            public DateTime LateDate { set; get; }
    
            [DisplayName("图标")]
            public string Icon { get; set; }
    
            [DisplayName("状态")]
            public int State { set; get; }
    
            [Description("用户实体集合")]
            public virtual ICollection<User> UserCollection { get; set; }
    
            [Description("控制权限实体集合")]
            public virtual ICollection<ActionPermission> ActionPermissionCollection { get; set; }
    
        }
    }
    复制代码

    5)用户后台管理员类

    复制代码
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel;
    
    namespace WL.Models.Permission
    {
        [DisplayName("用户成员")]
        [DisplayColumn("UserId")]
        public class User
        {
            [Key]
            [DisplayName("用户ID")]
            public int UserID { set; get; }
    
            [DisplayName("用户名称")]
            public string UserName { set; get; }
    
            [DisplayName("用户密码")]
            public string PassWord { set; get; }
    
            [DisplayName("用户邮件")]
            public string Mail { set; get; }
    
            [DisplayName("用户电话")]
            public string Phone { set; get; }
    
            [DisplayName("说明")]
            public string Description { set; get; }
    
            [DisplayName("创建时间")]
            public DateTime CreateDate { set; get; }
    
            [DisplayName("操作用户名")]
            public string Operator { set; get; }
    
            [DisplayName("最后修改时间")]
            public DateTime LateDate { set; get; }
    
            [DisplayName("图标")]
            public string Icon { get; set; }
    
            [DisplayName("状态")]
            public int State { set; get; }
    
            [Description("部门实体")]
            public virtual ICollection<DepartMent> DepartMentCollection { get; set; }
    
            [Description("角色实体")]
            public virtual ICollection<Role> RoleCollection { get; set; }
    
            [Description("控制权限实体集合")]
            public virtual ICollection<ActionPermission> ActionPermissionCollection { get; set; }
    
        }
    }
    复制代码

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

    并配置web.config

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

    设置默认项目

    在命令行里输入

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

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

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

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

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

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

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

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

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

    复制代码
      #region -----校验用户是否登录进入网站的-----
                base.OnActionExecuting(filterContext);
    
                HttpCookie UserInfo = System.Web.HttpContext.Current.Request.Cookies.Get("COOKIE_NAME_FOR_USER");
    
                //检验用户是否已经登录,如果登录则不执行,否则则执行下面的跳转代码
                if (UserInfo == null)
                {
                    filterContext.Result = RedirectToRoute(new { Controller = "Login", Action = "Index" });
                    return;
                }
                #endregion
    
                var UserInfoStr = UserInfo["COOKIE_NAME_FOR_USER_INFO"].ToString();
    
                if (string.IsNullOrEmpty(UserInfoStr))
                {
                    filterContext.Result = RedirectToRoute(new { Controller = "Login", Action = "Index" });
                    return;
                }
    复制代码

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

    复制代码
       var userNameArray = UserInfoStr.Split(new string[] { "$|$" }, StringSplitOptions.RemoveEmptyEntries);
                var username = userNameArray[0];
                var pwdword = userNameArray[1];
    
                CurrentUserInfo = this.userInfoService.LoadEntites(u => username.Equals(u.UserName) && pwdword.ToLower().Equals(u.PassWord.ToLower()) && u.State == 0).FirstOrDefault();
    
                if (CurrentUserInfo == null)
                {
                    filterContext.Result = RedirectToRoute(new { Controller = "Login", Action = "Index" });
                    return;
                }
    复制代码

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

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

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

    角户权限判断

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

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

    复制代码
                //分析线路 User->Action
                var user = (from c in userCurrent.ActionPermissionCollection
                            where c.ActionPermissionName.ToLower() == action && c.ControllerPermissionName.ToLower() == controller && c.State == 0
                            select c).FirstOrDefault();
    
                if (user != null)
                    return;
    复制代码

    其它可能的错误

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

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

    完整的代码:

    复制代码
                #region -----校验用户是否登录进入网站的-----
                base.OnActionExecuting(filterContext);
    
                HttpCookie UserInfo = System.Web.HttpContext.Current.Request.Cookies.Get("COOKIE_NAME_FOR_USER");
    
                //检验用户是否已经登录,如果登录则不执行,否则则执行下面的跳转代码
                if (UserInfo == null)
                {
                    filterContext.Result = RedirectToRoute(new { Controller = "Login", Action = "Index" });
                    return;
                }
                #endregion
    
                var UserInfoStr = UserInfo["COOKIE_NAME_FOR_USER_INFO"].ToString();
    
                if (string.IsNullOrEmpty(UserInfoStr))
                {
                    filterContext.Result = RedirectToRoute(new { Controller = "Login", Action = "Index" });
                    return;
                }
    
                var userNameArray = UserInfoStr.Split(new string[] { "$|$" }, StringSplitOptions.RemoveEmptyEntries);
                var username = userNameArray[0];
                var pwdword = userNameArray[1];
    
                CurrentUserInfo = this.userInfoService.LoadEntites(u => username.Equals(u.UserName) && pwdword.ToLower().Equals(u.PassWord.ToLower()) && u.State == 0).FirstOrDefault();
    
                if (CurrentUserInfo == null)
                {
                    filterContext.Result = RedirectToRoute(new { Controller = "Login", Action = "Index" });
                    return;
                }
    
                #region -------检验用户是否有访问此地址的权利----
                //先将当前的请求,到权限表里面去找对应的数据
                System.Web.Routing.RouteData Rotedate = filterContext.RequestContext.RouteData;
                string controller = (RouteData.Values["controller"] ?? "").ToString().ToLower();
                string action = (RouteData.Values["action"] ?? "").ToString().ToLower();
    
                //默认
                if (HasDefaultAction(filterContext))
                    return;
    
                //然后和权限表进行对比,如果取出来则通过请求,否则不通过
                //取出当前权限的数据
                //想去用户权限表里面查询有没有数据
                //分析线路 User->Role->Action
                //拿到当前的用户信息
                var userCurrent = userInfoService.LoadEntites(u => u.UserID == CurrentUserInfo.UserID).FirstOrDefault();
                var role = (from r in userCurrent.RoleCollection
                            from c in r.ActionPermissionCollection
                            where c.ActionPermissionName.ToLower() == action && c.ControllerPermissionName.ToLower() == controller && c.State == 0
                            select r).FirstOrDefault();
                if (role != null)
                    return;
    
                //分析线路 User->Action
                var user = (from c in userCurrent.ActionPermissionCollection
                            where c.ActionPermissionName.ToLower() == action && c.ControllerPermissionName.ToLower() == controller && c.State == 0
                            select c).FirstOrDefault();
    
                if (user != null)
                    return;
    
                EndRequest(filterContext);
    复制代码

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

    简单的总结下思路:

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

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

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

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

     
  • 相关阅读:
    文件上传跨域解决方案-jQuery-File-Upload
    求数列的的增幅,已知起始列和结束列,中间阶梯数
    mud那些坑 find_object问题
    jquery mCustomScrollbar使用
    JS的Date对象
    JS获取当前日期
    Go数组求和
    Go九九乘法表
    Go语言基础之time包
    Go语言基础之流程控制
  • 原文地址:https://www.cnblogs.com/xhxsk/p/9024836.html
Copyright © 2020-2023  润新知