尝试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[];
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,那么,说明不需要权限控制,直接放行。
本来想在一篇文章写完所有的权限内容的,写到此,文章太够长了,只能期待下篇。
下篇文章我们主要写各种控制器的具体控制,文章很长,请细细品尝哦。
出处:http://www.cnblogs.com/laogu2/ 欢迎转载,但任何转载必须保留完整文章,在显要地方显示署名以及原文链接。如您有任何疑问或者授权方面的协商,请给我留言。
尝试asp.net mvc 基于controller action 方式权限控制方案可行性的更多相关文章
- 尝试asp.net mvc 基于controller action 方式权限控制方案可行性(转载)
微软在推出mvc框架不久,短短几年里,版本更新之快,真是大快人心,微软在这种优秀的框架上做了大量的精力投入,是值得赞同的,毕竟程序员驾驭在这种框架上,能够强力的精化代码,代码层次也更加优雅,扩展较为方 ...
- 返璞归真 asp.net mvc (3) - Controller/Action
原文:返璞归真 asp.net mvc (3) - Controller/Action [索引页] [源码下载] 返璞归真 asp.net mvc (3) - Controller/Action 作者 ...
- ASP.NET MVC中controller和view相互传值的方式
ASP.NET MVC中Controller向view传值的方式: ViewBag.ViewData.TempData 单个值的传递 Json 匿名类型 ExpandoObject Cookie Vi ...
- ASP.NET MVC 基于角色的权限控制系统的示例教程
上一次在 .NET MVC 用户权限管理示例教程中讲解了ASP.NET MVC 通过AuthorizeAttribute类的OnAuthorization方法讲解了粗粒度控制权限的方法,接下来讲解基于 ...
- ASP.NET MVC基于标注特性的Model验证:将ValidationAttribute应用到参数上
原文:ASP.NET MVC基于标注特性的Model验证:将ValidationAttribute应用到参数上 ASP.NET MVC默认采用基于标准特性的Model验证机制,但是只有应用在Model ...
- ASP.NET MVC基于标注特性的Model验证:一个Model,多种验证规则
原文:ASP.NET MVC基于标注特性的Model验证:一个Model,多种验证规则 对于Model验证,理想的设计应该是场景驱动的,而不是Model(类型)驱动的,也就是对于同一个Model对象, ...
- 【部分】ASP.NET MVC的Controller接收输入详解
原文:https://blog.csdn.net/lxrj2008/article/details/79455360 ASP.NET mvc的Controller要正确的响应用户发出的请求就要获取到用 ...
- Asp.net MVC 中Controller返回值类型ActionResult
[Asp.net MVC中Controller返回值类型] 在mvc中所有的controller类都必须使用"Controller"后缀来命名并且对Action也有一定的要求: 必 ...
- 在ASP.NET MVC中以post方式传递数组参数的示例
最近在工作中用到了在ASP.NET MVC中以post方式传递数组参数的情况,记录下来,以供参考. 一.准备参数对象 在本例中,我会传递两个数组参数:一个字符串数组,一个自定义对象数组.这个自定义对象 ...
随机推荐
- [C#] async 的三大返回类型
async 的三大返回类型 序 博主简单数了下自己发布过的异步文章,已经断断续续 8 篇了,这次我想以 async 的返回类型为例,单独谈谈. 异步方法具有三个可让开发人员选择的返回类型:Task&l ...
- java字符乱码
在java中处理字符时,经常会发生乱码,而主要出现的地方在读取文本文件时发生,或者是写入到文件中,在其他地方打开乱码. 如下例子: BufferedReader br = null; try { br ...
- HTTPS简介
一.简单总结 1.HTTPS概念总结 HTTPS 就是对HTTP进行了TLS或SSL加密. 应用层的HTTP协议通过传输层的TCP协议来传输,HTTPS 在 HTTP和 TCP中间加了一层TLS/SS ...
- Android性能优化之巧用软引用与弱引用优化内存使用
前言: 从事Android开发的同学都知道移动设备的内存使用是非常敏感的话题,今天我们来看下如何使用软引用与弱引用来优化内存使用.下面来理解几个概念. 1.StrongReference(强引用) 强 ...
- 设置tomcat远程debug
查看端口占用情况命令: netstat -tunlp |grep 8000 tomcat 启动远程debug: startup.sh 中的最后一行 exec "$PRGDIR"/& ...
- CGI与FastCGI nginx+PHP-FPM
本文转载自CGI与FastCGI 1.当我们在谈到cgi的时候,我们在讨论什么 最早的Web服务器简单地响应浏览器发来的HTTP请求,并将存储在服务器上的HTML文件返回给浏览器,也就是静态html. ...
- 【踩坑速记】二次依赖?android studio编译运行各种踩坑解决方案,杜绝弯路,总有你想要的~
这篇博客,只是把自己在开发中经常遇到的打包编译问题以及解决方案给大家稍微分享一下,不求吸睛,但求有用. 1.大家都知道我们常常会遇到dex超出方法数的问题,所以很多人都会采用android.suppo ...
- 小兔JS教程(三)-- 彻底攻略JS回调函数
这一讲来谈谈回调函数. 其实一句话就能概括这个东西: 回调函数就是把一个函数当做参数,传入另一个函数中.传进去的目的仅仅是为了在某个时刻去执行它. 如果不执行,那么你传一个函数进去干嘛呢? 就比如说对 ...
- 用MongoDB分析合肥餐饮业
看了<从数据角度解析福州美食>后难免心痒,动了要分析合肥餐饮业的念头,因此特地写了Node.js爬虫爬取了合肥的大众点评数据.分析数据库我并没有采用MySQL而是用的MongoDB,是因为 ...
- .NET Core 2016 回顾
都在回顾自己的2016,今天我们来看看.NET Core的2016. 每一年的脚步的确是快,转眼间马上就2017.新的一年,带着理想和抱负继续出发. 1 月 ASP.NET 5 改名 ASP.NET ...