系列目录html
这一节咱们来跑通整个系统,验证的流程,经过AOP切入方式,在访问方法以前,执行一个验证机制来判断是否有操做权限(如:增删改等)数据库
原理:经过MVC自带筛选器,在筛选器分解路由的Action和controller来验证是否有权限。编程
首先咱们要理解一下筛选器缓存
筛选器的由来及用途
有时,您须要在调用操做方法以前或运行操做方法以后执行逻辑。安全
为了对此提供支持,ASP.NET MVC 提供了筛选器。 筛选器是自定义类,可提供用于向控制器操做方法添加操做前行为和操做后行为的声明性和编程性手段。session
ASP.NET MVC 支持如下类型的操做筛选器:框架
受权筛选器。 这些筛选器用于实现 IAuthorizationFilter 和作出关因而否执行操做方法(如执行身份验证或验证请求的属性)的安全决策。 AuthorizeAttribute 类和 RequireHttpsAttribute 类是受权筛选器的示例。 受权筛选器在任何其余筛选器以前运行。ide
操做筛选器。 这些筛选器用于实现 IActionFilter 以及包装操做方法执行。 IActionFilter 接口声明两个方法:OnActionExecuting 和 OnActionExecuted。 OnActionExecuting 在操做方法以前运行。 OnActionExecuted 在操做方法以后运行,能够执行其余处理,如向操做方法提供额外数据、检查返回值或取消执行操做方法。测试
结果筛选器。 这些筛选器用于实现 IResultFilter 以及包装 ActionResult 对象的执行。 IResultFilter 声明两个方法:OnResultExecuting 和 OnResultExecuted。 OnResultExecuting 在执行 ActionResult 对象以前运行。 OnResultExecuted 在结果以后运行,能够对结果执行其余处理,如修改 HTTP 响应。 OutputCacheAttribute 类是结果筛选器的一个示例。ui
异常筛选器。 这些筛选器用于实现 IExceptionFilter,并在 ASP.NET MVC 管道执行期间引起了未处理的异常时执行。 异常筛选器可用于执行诸如日志记录或显示错误页之类的任务。 HandleErrorAttribute 类是异常筛选器的一个示例。
建立自定义操做筛选器
框架将先调用操做筛选器的 OnActionExecuting 方法,而后再调用以操做筛选器特性标记的任意操做方法。 一样,该框架将在操做方法完成后调用 OnActionExecuted 方法。
调用 OnResultExecuting 方法后,要当即调用您的操做返回的 ActionResult 实例。 执行结果后,紧接着就要调用 OnResultExecuted 方法。 这些方法对于执行日志记录、缓存输出结果之类的操做很是有用。
以上都是理论问题了,说到底咱们就是要OnActionExecuting利用这个方法
当一个Action被执行时调用OnActionExecuting判断是否有权限操做。
因为OnActionExecuting涉及到其余用户和权限的访问咱们须要添加SysUser和SysRight的BLL和DAL层了
咱们还须要一个存储过程[P_Sys_GetRightOperate]用于取模块的当前用户操做权限,这里为何用存储过程呢,快点呗,反正这快不用怎么维护了
存储过程以下:
USE db GO /****** Object: StoredProcedure [dbo].[P_Sys_GetRightOperate] Script Date: 12/01/2013 12:25:48 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO -- ============================================= -- Author: <Author,,Name> -- Create date: <Create Date,,> -- Description: <Description,,> -- ============================================= CREATE PROCEDURE [dbo].[P_Sys_GetRightOperate] @userId varchar(50),@url varchar(200) AS --取模块的当前用户操做权限 select distinct KeyCode,IsValid from SysRightOperate where RightId in( select a.id from SysRight a, SysModule b where RoleId in( select SysRoleId from SysRoleSysUser where SysUserId =@userId) and a.ModuleId = b.Id and b.Url =@url) and IsValid=1 GO
建立好了把存储过程更新到EF中去,EF5.0将自动建立一个复杂的类型,你们能够打开来看下
建立一个权限的类permModel,咱们将获取到的权限保存到这个类中去,这个类最终是一个一个的session转换而来的。
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace App.Models.Sys { public class permModel { public string KeyCode { get; set; }//操做码 public bool IsValid { get; set; }//是否验证 } }
SysUser的BLL层和SysRight的DAL层了,以下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using App.Models; using App.Models.Sys; namespace App.IDAL { public interface ISysRightRepository { List<permModel> GetPermission(string accountid, string controller); } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using App.IDAL; using App.Models; using App.Models.Sys; namespace App.DAL { public class SysRightRepository : ISysRightRepository,IDisposable { /// <summary> /// 取角色模块的操做权限,用于权限控制 /// </summary> /// <param name="accountid">acount Id</param> /// <param name="controller">url</param> /// <returns></returns> public List<permModel> GetPermission(string accountid, string controller) { using (DBContainer db = new DBContainer()) { List<permModel> rights = (from r in db.P_Sys_GetRightOperate(accountid, controller) select new permModel { KeyCode = r.KeyCode, IsValid = r.IsValid }).ToList(); return rights; } } public void Dispose() { } } }
GetPermission将经过存储过程访问取得数据
using System; using System.Collections.Generic; using System.Linq; using System.Text; using App.Models.Sys; using App.Common; using App.Models; namespace App.IBLL { public interface ISysUserBLL { List<permModel> GetPermission(string accountid, string controller); } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using App.BLL.Core; using App.IBLL; using Microsoft.Practices.Unity; using App.IDAL; using App.Models.Sys; using App.Common; using App.Models; using System.Transactions; namespace App.BLL { public class SysUserBLL : BaseBLL, ISysUserBLL { [Dependency] public ISysRightRepository sysRightRepository { get; set; } public List<permModel> GetPermission(string accountid, string controller) { return sysRightRepository.GetPermission(accountid,controller); } } }
能够把SysRightRepository变成SysUserRepository层,我这样作是为了区分一下而已,SysRight表明权限,SysUser是用户,根据不一样的用户获取他的权限
咱们建立一个筛选器在App.Admin下的Core建立SupportFilter.cs
添加以下代码:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Routing; using App.Models.Sys; using App.BLL; using App.DAL; namespace App.Admin { public class SupportFilterAttribute : ActionFilterAttribute { public string ActionName { get; set; } private string Area; public override void OnActionExecuted(ActionExecutedContext filterContext) { base.OnActionExecuted(filterContext); } /// <summary> /// Action加上[SupportFilter]在执行actin以前执行如下代码,经过[SupportFilter(ActionName="Index")]指定参数 /// </summary> /// <param name="filterContext">页面传过来的上下文</param> public override void OnActionExecuting(ActionExecutingContext filterContext) { //读取请求上下文中的Controller,Action,Id var routes = new RouteCollection(); RouteConfig.RegisterRoutes(routes); RouteData routeData = routes.GetRouteData(filterContext.HttpContext); //取出区域的控制器Action,id string ctlName = filterContext.Controller.ToString(); string[] routeInfo = ctlName.Split('.'); string controller = null; string action = null; string id = null; int iAreas = Array.IndexOf(routeInfo, "Areas"); if (iAreas > 0) { //取区域及控制器 Area = routeInfo[iAreas + 1]; } int ctlIndex = Array.IndexOf(routeInfo, "Controllers"); ctlIndex++; controller = routeInfo[ctlIndex].Replace("Controller", "").ToLower(); string url = HttpContext.Current.Request.Url.ToString().ToLower(); string[] urlArray = url.Split('/'); int urlCtlIndex = Array.IndexOf(urlArray, controller); urlCtlIndex++; if (urlArray.Count() > urlCtlIndex) { action = urlArray[urlCtlIndex]; } urlCtlIndex++; if (urlArray.Count() > urlCtlIndex) { id = urlArray[urlCtlIndex]; } //url action = string.IsNullOrEmpty(action) ? "Index" : action; int actionIndex = action.IndexOf("?", 0); if (actionIndex > 1) { action = action.Substring(0, actionIndex); } id = string.IsNullOrEmpty(id) ? "" : id; //URL路径 string filePath = HttpContext.Current.Request.FilePath; AccountModel account = filterContext.HttpContext.Session["Account"] as AccountModel; if (ValiddatePermission(account, controller, action, filePath)) { return; } else { filterContext.Result = new EmptyResult(); return; } } public bool ValiddatePermission(AccountModel account, string controller, string action, string filePath) { bool bResult = false; string actionName = string.IsNullOrEmpty(ActionName) ? action : ActionName; if (account != null) { List<permModel> perm = null; //测试当前controller是否已赋权限值,若是没有从 //若是存在区域,Seesion保存(区域+控制器) if (!string.IsNullOrEmpty(Area)) { controller = Area + "/" + controller; } perm = (List<permModel>)HttpContext.Current.Session[filePath]; if (perm == null) { using (SysUserBLL userBLL = new SysUserBLL() { sysRightRepository = new SysRightRepository() }) { perm = userBLL.GetPermission(account.Id, controller);//获取当前用户的权限列表 HttpContext.Current.Session[filePath] = perm;//获取的劝降放入会话由Controller调用 } } //当用户访问index时,只要权限>0就能够访问 if (actionName.ToLower() == "index") { if (perm.Count > 0) { return true; } } //查询当前Action 是否有操做权限,大于0表示有,不然没有 int count = perm.Where(a => a.KeyCode.ToLower() == actionName.ToLower()).Count(); if (count > 0) { bResult = true; } else { bResult = false; HttpContext.Current.Response.Write("你没有操做权限,请联系管理员!"); } } return bResult; } public override void OnResultExecuted(ResultExecutedContext filterContext) { base.OnResultExecuted(filterContext); } public override void OnResultExecuting(ResultExecutingContext filterContext) { base.OnResultExecuting(filterContext); } } }
这是一个筛选器。全部逻辑代码都在这里了
打开SysSampleController
修改Create方法变为如下
[SupportFilter] public ActionResult Create() { return View(); }
回到筛选器public string ActionName { get; set; },其中ActionName是自定义Action的名称,好比在Create中直接[SupportFilter]那么ActionName取得就是Create,这将和你的数据库操做码进行对应的,那么个人方法是CreateAttr,那么要使用Create这个操做码,怎么办
那么就是
[SupportFilter(ActionName = "Create")] public ActionResult CreateAttr()
那么相似的写法
[SupportFilter(ActionName = "Index")] public JsonResult GetList()
Index无需填写操做码将自动建立操做码,若是你拥有一个操做码那么index将被受权,这个是咱们与系统之间的一个约定(你能够去掉这个约定,修改代码便可)
假如你拥有增删改权限却没有访问列表的权限,那不是...
OnActionExecuting负责分解,交给ValiddatePermission去生成权限
若是写在Areas区域的也是兼容的,已经作了处理。
若是你越权操做那么将执行 HttpContext.Current.Response.Write("你没有操做权限,请联系管理员!");
目前位置咱们已经跑通了整个系统了,接下来就是自动化的用户角色之间的受权和模块的制做了,能跑通,其余都是很简单了,对吧
这一章比较复杂,须要对AOP编程,MVC的筛选器,和路由进行了解,才能读的比较顺。
若是你没有读懂,那么代码敲一遍,那么你也就差很少知道了
代码进行了大量的注释,还不懂那么留言。
目前为止,咱们一个基于按钮级别的权限系统已经所有跑通,如今,能够建立一些没有权限的Action来验证了
我建立:(很明显咱们数据库没有这个test的 action的权限),因此你别想越权操做了
[SupportFilter] public ActionResult Test() { return View(); }
最后预览
咱们预览一个有权限的
感谢你们,花了你宝贵的时间阅读这一节。