and logs rmi hmm return 管理 modify list
之前一直没怎么接触过权限验证这块,刚好公司老平台改版,就有了这篇权限验证。此篇文章大致讲解下 精确到按钮级别的验证如何实现、以及权限验证设计的参考思路(菜鸟一枚,大神勿喷)。
在开发大项目的时候总会有相关的 AOP 面向切面编程的组件,而 MVC(特指:Asp.Net MVC,以下皆同)项目中不想让 MVC 开发人员去关心和写类似身份验证,日志,异常,行为截取等这部分重复的代码,那我们可以通过 AOP 截取实现,而在 MVC 项目中我们就可以直接使用它提供的 Filter 的特性帮我们解决,不用自己实现复杂的 AOP 了。
在 Asp.NET Mvc 中当你有以下及类似以下需求时你可以使用 Filter(过滤器)功能
光说不练假把式,下面我给大家一个示例, 来看看它们的执行顺序,首先添加一个普通的类 TestFilterAttribute,这个类要继承 ActionFilterAttribute,代码如下:
- 1 /// <summary>
- 2 /// Filter 执行顺序Test,需要继承筛选器的基类ActionFilterAttribute
- 3 /// </summary>
- 4 public class TestFilterAttribute : ActionFilterAttribute
- 5 {
- 6 public stringMessage {get;set; }
- 7
- 8 public override void OnActionExecuting(ActionExecutingContext filterContext)
- 9 {
- 10 base.OnActionExecuting(filterContext);
- 11filterContext.HttpContext.Response.Write("Action执行之前"+ Message +"<br />");
- 12 }
- 13
- 14 public override void OnActionExecuted(ActionExecutedContext filterContext)
- 15 {
- 16 base.OnActionExecuted(filterContext);
- 17filterContext.HttpContext.Response.Write("Action执行之后"+ Message +"<br />");
- 18 }
- 19
- 20 public override void OnResultExecuting(ResultExecutingContext filterContext)
- 21 {
- 22 base.OnResultExecuting(filterContext);
- 23filterContext.HttpContext.Response.Write("返回Result之前"+ Message +"<br />");
- 24 }
- 25
- 26 public override void OnResultExecuted(ResultExecutedContext filterContext)
- 27 {
- 28 base.OnResultExecuted(filterContext);
- 29filterContext.HttpContext.Response.Write("返回Result之后"+ Message +"<br />");
- 30 }
- 31}
写完这个代码后,我们回到 Action 上,打上上面的标记如下所示:
- 1[TestFilterAttribute(Message ="Action")]
- 2 public ActionResult Index()
- 3 {
- 4HttpContext.Response.Write("Action正在执行···<br />");
- 5 returnContent("正在返回Result···<br />");
- 6}
然后通过浏览器访问上面的 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 />");
- returnContent("正在返回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 stringMessage {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。
规定页面的访问形式,如
- [AcceptVerbs(HttpVerbs.Post)]
- public ActionResult Example(){
- return View();
- }
页面只能以 Post 形式访问,即表单提交。
规定 Action 的名称。
应用场景:如果不想用方法名做为 Action 名,或 Action 名为关键字的话,如
- [ActionName("class")]
- public ActionResult Example(){
- return View();
- }
当前方法仅是普通方法不解析为 Action
为 Action 添加缓存
- [OutputCache(Duration = 60, VaryByParam = "*")]
- public ActionResult Example()
- {
- return View();
- }
该 Action 可以接受 Html 等危险代码(ASP.NET MVC 在 aspx 中设置 <%@ Page 的属性无法完成等同任务。)
- [ValidateInput(false)]
- public ActionResult Example()
- {
- return View();
- }
用于验证服务器篡改。
- [ValidateAntiForgeryToken]
- public ActionResult Example()
- {
- return View();
- }
经过这一篇文章的介绍我们大体了解了 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 boolAuthorityCheck(stringkeyWork,stringuserName,stringModuleName,boolMenu_button=false)
- {
- List userRoleOper = GetUserPermission(userName, ModuleName);
- if(userRoleOper !=null)
- {
- foreach(UsersRolesOper itemin 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 是返回的 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 = 0) {
- 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 == 0) {} 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)]
- publicActionResult DoneExport(DoneDataSearchView entity =null)
- {
- if(!ViewBag.isPersimion)
- {
- returnContent("<script>alert('您没有操作权限');location.href='/GoldAccounts/DoneList'</script>");
- }
- try
- {
- byte[] file =this._accService.GetDoneExportDT(entity);
- if(file ==null)
- returnRedirect("/GoldAccounts/DoneList");
- else
- returnFile(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)]
- publicActionResult PendingList(string id)
- {
- try
- {
- if(!ViewBag.isPersimion)
- {
- returnRedirect("/Home/Limit");
- }
- return View();
- }
- catch (Exception ex)
- {
- throw ex;
- }
- }
最后附一张效果图:
C# MVC 权限验证
来源: http://www.bubuko.com/infodetail-2088428.html