C# MVC权限验证
前言
之前一直没怎么接触过权限验证这块,刚好公司老平台改版,就有了这篇权限验证。此篇文章大致讲解下 精确到按钮级别的验证如何实现、以及权限验证设计的参考思路(菜鸟一枚,大神勿喷)。
在开发大项目的时候总会有相关的AOP面向切面编程的组件,而MVC(特指:Asp.Net MVC,以下皆同)项目中不想让MVC开发人员去关心和写类似身份验证,日志,异常,行为截取等这部分重复的代码,那我们可以通过AOP截取实现,而在MVC项目中我们就可以直接使用它提供的Filter的特性帮我们解决,不用自己实现复杂的AOP了。
在Asp.NET Mvc中当你有以下及类似以下需求时你可以使用Filter(过滤器)功能
- 判断登录与否或用户权限
- 决策输出缓存
- 防盗链
- 防蜘蛛
- 本地化与国际化设置
- 实现动态Action
第一节:知识储备
Asp.Net MVC提供了以下几种默认的Filter:
大家注意一点,Asp.Net MVC提供的ActionFilterAttribute默认实现了IActionFilter和IResultFilter。而ActionFilterAttribute是一个Abstract的类型,所以不能直接使用,因为它不能实例化,所以我们想使用它必须继承一下它然后才能使用。
Filter继承于ActionFilterAttribute抽象类,并可以覆写 void OnActionExecuting(ActionExecutingContext) voidOnActionExecuted(ActionExecutedContext) void OnResultExecuting(ResultExecutingContext) void OnResultExecuted(ResultExecutedContext)。
它们的执行先后顺序如下:
OnActionExecuting是Action执行前的操作
OnActionExecuted则是Action执行后的操作
OnResultExecuting是解析ActionResult前执行
OnResultExecuted是解析ActionResult后执行
接下来我们只要对以上的方法进行重写就可以在相应的步骤做一些操作了。
第二节:Filter实战
光说不练假把式,下面我给大家一个示例,来看看它们的执行顺序,首先添加一个普通的类TestFilterAttribute,这个类要继承ActionFilterAttribute,代码如下:
/// <summary>
/// Filter 执行顺序Test,需要继承筛选器的基类ActionFilterAttribute
/// </summary>
public class TestFilterAttribute : ActionFilterAttribute
{
public string Message { get; set; } public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
filterContext.HttpContext.Response.Write("Action执行之前" + Message + "<br />");
} public override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
filterContext.HttpContext.Response.Write("Action执行之后" + Message + "<br />");
} public override void OnResultExecuting(ResultExecutingContext filterContext)
{
base.OnResultExecuting(filterContext);
filterContext.HttpContext.Response.Write("返回Result之前" + Message + "<br />");
} public override void OnResultExecuted(ResultExecutedContext filterContext)
{
base.OnResultExecuted(filterContext);
filterContext.HttpContext.Response.Write("返回Result之后" + Message + "<br />");
}
}
写完这个代码后,我们回到Action上,打上上面的标记如下所示:
[TestFilterAttribute(Message = "Action")]
public ActionResult Index()
{
HttpContext.Response.Write("Action正在执行···<br />");
return Content("正在返回Result···<br />");
}
然后通过浏览器访问上面的Action便可以看到下面的执行顺序

总的执行顺序是:
Action执行前:OnActionExecuting方法先执行→Action执行→OnActionExecuted方法执行→OnResultExecuting方法执行→返回的ActionRsult中的executeResult方法执行→OnResultExecuted执行
最终显示的效果就是如上图所示。
感觉还不错吧!哈哈!这要想用到这个过滤机制的地方的时候,只要在Action上面添加标记便可以实现效果。
如果我们将此标签打到Controller上的话,TestFilterAttributeFilter将作用到Controller下的所有的Action。例如如下代码所示:
[TestFilter(Message="Controller")]
public class TestFilterController : Controller
{
//
// GET: /TestFilter/
[TestFilter(Message="Action")]
public ActionResult Index()
{
HttpContext.Response.Write("Action正在执行···<br />");
return Content("正在返回Result···<br />");
} }
如果单纯的按照上面的代码来做就有个问题了,我们再执行显示的页面会有什么情况呢?Controller上的Filter会执行吗?那标签的作用会执行两次吗?下面是最后的执行结果如下图所示:

结果说明
默认情况下Action上打了TestFilterAttribute 标签后,虽然在Controller上也打上了此标签,但它只有Action上的标签起作用了。
补充:如果Action没有打上TestFilterAttribute标签,那么Controller上的标签便会被执行。
Index 执行时,Filter的方法只执行了一次,而某些情况下我们也想让Controller上的FilterAttribute也执行一次TestFilterAttribute,那我们怎么才能让Controller上的[TestFilter(Message = "controller")]也起作用呢?
答案是:我们只需在TestFilterAttribute类的定义上打上标记[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]即可【下面类的最上面红色字体部分】,也就是让其成为可以多次执行的Action。代码如下:
/// <summary>
/// Filter 执行顺序Test,需要继承筛选器的基类ActionFilterAttribute
/// </summary
[AttributeUsage(AttributeTargets.All, AllowMultiple = true)] //打标记是让所有的Controller的标记生效
public class TestFilterAttribute : ActionFilterAttribute
{
public string Message { get; set; } public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
filterContext.HttpContext.Response.Write("Action执行之前" + Message + "<br />");
} public override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
filterContext.HttpContext.Response.Write("Action执行之后" + Message + "<br />");
} public override void OnResultExecuting(ResultExecutingContext filterContext)
{
base.OnResultExecuting(filterContext);
filterContext.HttpContext.Response.Write("返回Result之前" + Message + "<br />");
} public override void OnResultExecuted(ResultExecutedContext filterContext)
{
base.OnResultExecuted(filterContext);
filterContext.HttpContext.Response.Write("返回Result之后" + Message + "<br />");
}
}
浏览效果如下图:

我们看到的结果是Controller上的ActionFilter先于Action上打的标记执行。同样Result执行executeResult方法之前也是先执行Controller上的Filter标记中的OnResultexecuteing方法。
最后的执行顺序是:Controller上的OnActionExecuting→Action上的OnActionExecuting→Action执行→Action上的OnActionExecuted→Controller上的OnActionExecuted
到此Action就执行完毕了,我们看到是一个入栈出栈的顺序。后面是Action返回ActionResult后执行了ExecuteResult方法,但在执行之前要执行Filter。具体顺序为:
接上面→Controller的OnResultExecuting方法→Action上的OnResultExecuting→Action返回ActionResult后执行了ExecuteResult方法→Action上的OnResultExecuted执行→Controller上的OnResultExecuted执行→结束。
又接着一个问题也来了,我们想有些公共的方法需要每个Action都执行以下,而在所有的Controller打标记是很痛苦的。幸好Asp。Net MVC3带来了一个美好的东西,全局Filter。而怎么注册全局Filter呢?答案就在Global.asax中。让我们看以下代码,我是如何将上面我们定义的TestFilterAttribute 注册到全局Filter中。上代码:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
//注册全局过滤器
filters.Add(new TestFilterAttribute() { Message="全局"});
}
效果如下图:

我们看到的结果是全局的Action首先执行,然后才是Controller下的Filter执行,最后才是Action上的标签执行。当然这是在TestFilterAttribute类的定义上打上标记[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]的前提下。不然 如果Action打上了标签跟Controller的相同则它只会执行Action上的Filter。
第三节:其他Filter
AcceptVerbs
规定页面的访问形式,如
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Example(){
return View();
}
页面只能以Post形式访问,即表单提交。
ActionName
规定Action的名称。
应用场景:如果不想用方法名做为Action名,或Action名为关键字的话,如
[ActionName("class")]
public ActionResult Example(){
return View();
}
NonAction
当前方法仅是普通方法不解析为Action
OutputCache
为Action添加缓存
[OutputCache(Duration = 60, VaryByParam = "*")]
public ActionResult Example()
{
return View();
}
ValidateInput
该Action可以接受Html等危险代码(ASP.NET MVC在aspx中设置<%@ Page 的属性无法完成等同任务。)
[ValidateInput(false)]
public ActionResult Example()
{
return View();
}
ValidateAntiForgeryTokenAttribute
用于验证服务器篡改。
[ValidateAntiForgeryToken]
public ActionResult Example()
{
return View();
}
Filter总结
经过这一篇文章的介绍我们大体了解了Filter的使用方法,还了解到全局Filter的用法,尤其是当相同的Filter重复作用到同一个Action上时,如果没有设置可多次执行的标签那只有Action上的Filter执行,而Controller和全局Filter都被屏蔽掉,但是设置可多次执行,那首先执行全局Filter其次是Controller再次之就是Action上的Filter了。同时还了解了系统的Filter的用法。
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
万恶的分割线
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
权限设计

我们可以先来看看它们之间的关系
.....
可能大家看这个数据字典还不够直观那我们来看看下图描述的:

总体上分为:组织权限管理、管理员管理、功能菜单管理、管理组、菜单动作管理几部分。
注意:因为组和角色都具有上下级关系,所以下级的组或角色的权限只能在自己的直属上级的权限中选择,下级的组或者角色的总的权限都不能大于直属上级的总权限。
菜单和按钮权限
新建权限实体
public class UsersRolesOper
{
/// <summary>
/// 权限ID
/// </summary>
public int RoleID { get; set; }
/// <summary>
/// 菜单名称
/// </summary>
public string MenuName { get; set; }
/// <summary>
/// 菜单code
/// </summary>
public string MenuCode { get; set; }
/// <summary>
/// 菜单ID
/// </summary>
public int MenuID { get; set; }
/// <summary>
/// 动作ID
/// </summary>
public int ActionID { get; set; }
/// <summary>
/// 动作名称
/// </summary>
public string ActionName { get; set; }
/// <summary>
/// 动作Code
/// </summary>
public string ActionCode { get; set; }
}
新建过滤器类AuthorityCheck 实现权限验证
public class AuthorityCheck : ActionFilterAttribute
{
private readonly IRolesService _roles;public string KeyWork { get; set; }
public string MoudleName { get; set; }
public bool Menu_button { get; set; } public override void OnActionExecuting(ActionExecutingContext filterContext)
{
//之前执行
HttpResponseBase response = filterContext.HttpContext.Response;
if (_roles.AuthorityCheck(KeyWork, HttpContext.Current.User.Identity.Name, MoudleName,Menu_button))
{
filterContext.Controller.ViewBag.isPersimion = true;
}
else
{
filterContext.Controller.ViewBag.isPersimion = false;
}
base.OnActionExecuting(filterContext);
}
}
/// <summary>
/// 验证权限
/// </summary>
/// <param name="keyWork">传入code</param>
/// <param name="userName">用户</param>
/// <param name="ModuleName">菜单或按钮名称</param>
/// <param name="Menu_button">菜单还是按钮,false是菜单</param>
/// <returns></returns>
public bool AuthorityCheck(string keyWork, string userName, string ModuleName,bool Menu_button=false)
{
List<UsersRolesOper> userRoleOper = GetUserPermission(userName, ModuleName);
if (userRoleOper != null)
{
foreach (UsersRolesOper item in userRoleOper)
{
if (Menu_button)
{
if (item.ActionCode.ToLower() == keyWork.ToLower())
{
return true;
}
}
else
{
if (item.ActionName == "查看")
{
if (item.MenuCode.ToLower() == keyWork.ToLower())
{
return true;
}
}
}
}
return false;
}
else
{
return false;
}
}
GetUserPermission方法就不贴了,主要是从数据库读取用户权限,存放在List集合中
Action使用
如果Action是返回的Json,可以返回Json后再前台处理,其中ViewBag.isPersimion对应过滤器类中返回的结果。例如:
/// <summary>
/// 编辑用户保存所有资料
/// </summary>
/// <param name="model"></param>
/// <param name="type"></param>
/// <returns></returns>
[HttpPost]
[AuthorityCheck(KeyWork = "Modify", MoudleName = "修改",Menu_button=true)]
public ActionResult AccountEdit(GoldEditeView model, int type = )
{
ResponseData res = new ResponseData { Status = ResponseStatu.ERROR, Message = "保存出错" };
if (!LoginInfo.IsSA)
{
if (!ViewBag.isPersimion)
{
res = new ResponseData { Status = ResponseStatu.LIMIT, Message = "你没有权限进行此操作!" };
return MyJson(res);
}
}
try
{
bool flag = false;
if (model.Person.ID == )
{ }
else
{
model.PersonInfo.ModifiedBy = LoginInfo.UserName;
flag = _accService.UpdateGoldPerson(model);
}
if (flag)
res = new ResponseData { Status = ResponseStatu.SUCCESS, Message = "SUCCESS", Data = model.Person.ID }; }
catch (Exception ex)
{ res = new ResponseData { Status = ResponseStatu.SUCCESS, Message = "保存出错", Data = ex.Message };
}
return MyJson(res);
}
如果是window.location.href方式的Action,可以这样处理:
[HttpPost]
[AuthorityCheck(KeyWork = "Export", MoudleName = "导出",Menu_button=true)]
public ActionResult DoneExport(DoneDataSearchView entity = null)
{
if (!ViewBag.isPersimion)
{
return Content("<script>alert('您没有操作权限');location.href='/GoldAccounts/DoneList'</script>");
}
try
{
byte[] file = this._accService.GetDoneExportDT(entity);
if (file == null)
return Redirect("/GoldAccounts/DoneList");
else
return File(file, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "GoldAccounts_Excel" + DateTime.Now.ToString("yyyyMMddHHmm") + ".xls");
}
catch (Exception ex)
{ throw ex;
}
}
最后,如果菜单没有权限,可以直接跳转到另一个提示页面:
[AuthorityCheck(KeyWork = "paper_account", MoudleName = "开户受理", Menu_button = false)]
public ActionResult PendingList(string id)
{
try
{
if (!ViewBag.isPersimion)
{
return Redirect("/Home/Limit");
}
return View();
}
catch (Exception ex)
{
throw ex;
}
}
效果图
最后附一张效果图:
....
C# MVC权限验证的更多相关文章
- NET MVC权限验证
ASP.NET MVC权限验证 封装类 写该权限类主要目地 为了让权限配置更加的灵活,可以根据SQL.json.或者XML的方式来动态进行页面的访问控制,以及没有权限的相关跳转. 使用步骤 1.要建一 ...
- ASP.NET MVC权限验证 封装类
写该权限类主要目地 为了让权限配置更加的灵活,可以根据SQL.json.或者XML的方式来动态进行页面的访问控制,以及没有权限的相关跳转. 使用步骤 1.要建一个全局过滤器 //受权过滤器 publi ...
- 关于filter web api mvc 权限验证 这里说的够详细了。。。
参考:http://www.cnblogs.com/willick/p/3331520.html Filter(筛选器)是基于AOP(面向方面编程)的设计,它的作用是对MVC框架处理客户端请求注入额外 ...
- MVC权限验证过滤器
Action属性,权限设定属性 [AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = false)] ...
- .net web mvc 权限验证
这里分享MVC的权限验证,内容中可能存在一些,莫名其妙的方法,那些是以前封装好的,大致可以根据方法名称知道他的意思. using Game.Entity; using Game.Entity.Plat ...
- mvc 权限验证
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.We ...
- MVC权限验证之ActionFilterAttribute
参考:http://www.cnblogs.com/waitingfor/archive/2011/12/27/2303784.html ActionFilterAttribute是Action过滤类 ...
- mvc权限验证--AuthorizeAttribute
在做后台管理时用户登录后就需要验证哪些权限了,没有登录的就直接退出到登录页面. 系统有自带的权限[Authorize],可用于几个地方: 1.将属性[Authorize]置于相关的action上方,验 ...
- Asp.net MVC 权限验证,以及是否允许匿名访问
public class CheckUserAttribute : ActionFilterAttribute, IAuthorizationFilter { public void OnAuthor ...
随机推荐
- Oracle的基本学习(三)—函数
一.字符函数 1.大小写控制函数 --lower:使字母变为小写-- --upper:使字母变为大写-- --initcap:使字符的第一个字母变为大写-- select lower('ABC') ...
- JavaScript基础学习(五)—其他引用类型
JavaScript定义了两个内置对象: Global和Math. 一.Global对象 1.URI编码方法 Global对象的encodeURI()和encodeURICompo ...
- JavaWeb总结(十)—文件上传和下载
一.文件的上传 1.文件的基本上传 对于文件上传,浏览器在上传的过程中是将文件以流的形式提交到服务器端的,如果直接使用Servlet获取上传文件的输入流然后再解析里面的请求参数是比较麻烦,所以一般选择 ...
- Oracle 数据导入导出操作 (转)
Oracle数据导入导出imp/exp 功能:Oracle数据导入导出imp/exp就相当与oracle数据还原与备份. 大多情况都可以用Oracle数据导入导出完成数据的备份和还原(不会造成数据的丢 ...
- 线段树(hdu 2795)
Billboard Time Limit: 20000/8000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total ...
- struts2 之 struts2数据校验
1. 数据校验一般分为2类:前端的校验(js校验),后端的校验(java代码):实际开发中大部分情况下都是采用js校验.在对数据安全要求较高的情况下可能会采用后端验证. 2. Struts2提供了后 ...
- 使用纯CSS方案,解决垂直居中
CSS是HTML元素的剪刀手,它极度的丰富了web页面的修饰.在众多CSS常见的样式需求中,有一奇葩式的存在[垂直居中],因为不管是从逻辑实现方面还是从正常需求量来讲,这都没理由让这个需求在实践过程中 ...
- tcp并发服务端
TCP并发服务器:并发服务器的思想是每一个客户端的请求并不由服务器的主进程直接处理,而是服务器主进程创建一个子进程来处理. 创建TCP并发服务器的算法如下: socket(……): //创建一个TCP ...
- MySQL最常用日期时间函数
日期和时间函数 可能的需求: 当前时间是多少.下个月的今天是星期几.统计截止到当前日期前 3 天的收入总和-- 上述需求就需要使用日期和时间函数来实现: MySQL服务器中的三种时区设置: ①系统时区 ...
- C语言学习第七章
今天开始学习指针,指针在C语言中具有很重要的地位,按照老师所说,学C学不好指针跟没学一样,可见指针在C语言中的重要地位.废话不多说,首先我们先要知道什么是指针. 指针:指针是一个变量,它存储另一个对象 ...