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

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

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

看看我们想要达到的效果

1)权限列表

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

2)菜单权限列表

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

3)角色权限列表

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

4)用户权限列表

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

5)主从菜单的配置

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

6)图标的*定制

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

我们添加,添加以后几个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

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

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

并配置web.config

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

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

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

设置默认项目

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

在命令行里输入

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

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

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

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

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

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

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

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

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

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

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

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[];
var pwdword = userNameArray[]; CurrentUserInfo = this.userInfoService.LoadEntites(u => username.Equals(u.UserName) && pwdword.ToLower().Equals(u.PassWord.ToLower()) && u.State == ).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 > )
{
//更具自定义的特性得到需要调用的类名与方法名
PermissionAttribute u = astri[] 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 ==
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 ==
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[];
var pwdword = userNameArray[]; CurrentUserInfo = this.userInfoService.LoadEntites(u => username.Equals(u.UserName) && pwdword.ToLower().Equals(u.PassWord.ToLower()) && u.State == ).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 ==
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 ==
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/ 欢迎转载,但任何转载必须保留完整文章,在显要地方显示署名以及原文链接。如您有任何疑问或者授权方面的协商,请给我留言
上一篇:delphi 一个线程和主界面的交互的演示代码


下一篇:Source Insight 4.0常用设置