【无私分享:从入门到精通ASP.NET MVC】从0开始,一起搭框架、做项目(6) 控制器基类 主要做登录用户、权限认证、日志记录等工作
索引
【无私分享:从入门到精通ASP.NET MVC】从0开始,一起搭框架、做项目 目录索引
简述
今天我们来写一个控制器基类 主要做登录用户、权限认证、日志记录等工作
项目准备
我们用的工具是:VS 2013 + SqlServer 2012 + IIS7.5
希望大家对ASP.NET MVC有一个初步的理解,理论性的东西我们不做过多解释,有些地方不理解也没关系,会用就行了,用的多了,用的久了,自然就理解了。
项目开始
很多朋友在前几篇都遇到 根节点 的问题,我把这几个xml先给大家贴一下
WebPage/Config下面三个XML
ComControllers.xml、IndexControllers.xml
1 <?xml version="1.0" encoding="utf-8" ?> 2 <objects xmlns="http://www.springframework.net"> 3 <description>Spring注入控制器,容器指向Service层封装的接口</description> 4 </objects>
Controllers.xml
1 <?xml version="1.0" encoding="utf-8" ?> 2 <objects xmlns="http://www.springframework.net"> 3 <description>Spring注入控制器,容器指向Service层封装的接口</description> 4 <!--系统管理 Begin--> 5 <!--登录控制器--> 6 <object type="WebPage.Areas.SysManage.Controllers.AccountController,WebPage" singleton="false"> 7 <property name="UserManage" ref="Service.User"/> 8 </object> 9 <!--系统管理 end--> 10 </objects>
Service/Config下面两个XML
ComService.xml
1 <?xml version="1.0" encoding="utf-8" ?> 2 <objects xmlns="http://www.springframework.net"> 3 <description>Spring注入Service,容器指向本层层封装的接口,舍弃Dao层,减少代码量</description> 4 </objects>
Service.xml
1 <?xml version="1.0" encoding="utf-8" ?> 2 <objects xmlns="http://www.springframework.net"> 3 <description>Spring注入Service,容器指向本层层封装的接口,舍弃Dao层,减少代码量</description> 4 <!--系统管理begin--> 5 <!--用户管理--> 6 <object id="Service.User" type="Service.ServiceImp.UserManage,Service" singleton="false"> 7 </object> 8 <!--系统管理end--> 9 </objects>
一、我们在Controllers文件夹下新建一个控制器 BaseController, 用于控制器基类,主要做登录用户、权限认证、日志记录等工作
二、我们声明一些公共变量和容器
变量主要用于我们查询分页的时候用户传递关键字、页码和分页条数
这个用户容器 主要是用户后台用户的一些操作
1 #region 公用变量 2 /// <summary> 3 /// 查询关键词 4 /// </summary> 5 public string keywords { get; set; } 6 /// <summary> 7 /// 视图传递的分页页码 8 /// </summary> 9 public int page { get; set; } 10 /// <summary> 11 /// 视图传递的分页条数 12 /// </summary> 13 public int pagesize { get; set; } 14 /// <summary> 15 /// 用户容器,公用 16 /// </summary> 17 public IUserManage UserManage = Spring.Context.Support.ContextRegistry.GetContext().GetObject("Service.User") as IUserManage; 18 #endregion
三、获取当前用户对象
从Sesssion中获取用户对象,Session过期后 通过 Cookies重新获取用户对象
1 #region 用户对象 2 /// <summary> 3 /// 获取当前用户对象 4 /// </summary> 5 public Account CurrentUser 6 { 7 get 8 { 9 //从Session中获取用户对象 10 if (SessionHelper.GetSession("CurrentUser") != null) 11 { 12 return SessionHelper.GetSession("CurrentUser") as Account; 13 } 14 //Session过期 通过Cookies中的信息 重新获取用户对象 并存储于Session中 15 var account = UserManage.GetAccountByCookie(); 16 SessionHelper.SetSession("CurrentUser", account); 17 return account; 18 } 19 } 20 #endregion
四、重写控制器 OnActionExecuting(ActionExecutingContext filterContext)方法 实现登录验证和公共变量的获取
protected override void OnActionExecuting(ActionExecutingContext filterContext)
1 #region 登录用户验证 2 //1、判断Session对象是否存在 3 if (filterContext.HttpContext.Session == null) 4 { 5 filterContext.HttpContext.Response.Write( 6 " <script type='text/javascript'> alert('~登录已过期,请重新登录');window.top.location='/'; </script>"); 7 filterContext.RequestContext.HttpContext.Response.End(); 8 filterContext.Result = new EmptyResult(); 9 return; 10 } 11 //2、登录验证 12 if (this.CurrentUser == null) 13 { 14 filterContext.HttpContext.Response.Write( 15 " <script type='text/javascript'> alert('登录已过期,请重新登录'); window.top.location='/';</script>"); 16 filterContext.RequestContext.HttpContext.Response.End(); 17 filterContext.Result = new EmptyResult(); 18 return; 19 } 20 21 #endregion
1 #region 公共Get变量 2 //分页页码 3 object p = filterContext.HttpContext.Request["page"]; 4 if (p == null || p.ToString() == "") { page = 1; } else { page = int.Parse(p.ToString()); } 5 6 //搜索关键词 7 string search = filterContext.HttpContext.Request.QueryString["Search"]; 8 if (!string.IsNullOrEmpty(search)) { keywords = search; } 9 //显示分页条数 10 string size = filterContext.HttpContext.Request.QueryString["example_length"]; 11 if (!string.IsNullOrEmpty(size) && System.Text.RegularExpressions.Regex.IsMatch(size.ToString(), @"^\d+$")) { pagesize = int.Parse(size.ToString()); } else { pagesize = 10; } 12 #endregion
五、模块权限验证功能
规则:1、根据模块别名验证对应模块
2、根据模块操作Action 验证是否可操作按钮
这里我们分为两个打步骤:第一,前台按钮没有相应操作权限的,我们移除前台操作按钮。
第二,也是为了防止用户绕过前台验证,我们对后台模块以及方法进行验证,如果用户对相应的模块没有相应的操作权限(添加、修改、删除、审核、发布等等,包含自定义操作类型),我们拒绝执行。
网站的权限判断是一个非常普遍的需求,我们实现这样的需求只要从 AuthorizeAttribute 集成,重写相关的判断逻辑
我们新建一个权限验证类UserAuthorizeAttribute 继承 AuthorizeAttribute (关于AuthorizeAttribute 点击这里)
public class UserAuthorizeAttribute : AuthorizeAttribute
我们对添加一个自定义的Attribute,通过AttributeUsage的Attribute来限定Attribute 所施加的元素的类型
作为参数的AttributeTarges的值允许通过“或”操作来进行多个值得组合,如果你没有指定参数,那么默认参数就是All 。
AttributeUsage除了继承Attribute 的方法和属性之外,还定义了以下三个属性:
AllowMultiple: 读取或者设置这个属性,表示是否可以对一个程序元素施加多个Attribute 。
Inherited:读取或者设置这个属性,表示是否施加的Attribute 可以被派生类继承或者重载。
ValidOn: 读取或者设置这个属性,指明Attribute 可以被施加的元素的类型。
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)] public class UserAuthorizeAttribute : AuthorizeAttribute {
}
添加一些字段和属性,并且实例化基类
1 #region 字段和属性 2 /// <summary> 3 /// 模块别名,可配置更改 4 /// </summary> 5 public string ModuleAlias { get; set; } 6 /// <summary> 7 /// 权限动作 8 /// </summary> 9 public string OperaAction { get; set; } 10 /// <summary> 11 /// 权限访问控制器参数 12 /// </summary> 13 private string Sign { get; set; } 14 /// <summary> 15 /// 基类实例化 16 /// </summary> 17 public BaseController baseController = new BaseController(); 18 19 #endregion
我们重写 AuthorizeAttribute 的 OnAuthorization(AuthorizationContext filterContext)方法
1 /// <summary> 2 /// 权限认证 3 /// </summary> 4 public override void OnAuthorization(AuthorizationContext filterContext) 5 { 6 }
分为四步:
1、判断模块是否对应
2、判断用户是否存在
3、调用下面的方法,验证是否有访问此页面的权限,查看加操作
4、有权限访问页面,将此页面的权限集合传给页面
1 //1、判断模块是否对应 2 if (string.IsNullOrEmpty(ModuleAlias)) 3 { 4 filterContext.HttpContext.Response.Write(" <script type='text/javascript'> alert('^您没有访问该页面的权限!'); </script>"); 5 filterContext.RequestContext.HttpContext.Response.End(); 6 filterContext.Result = new EmptyResult(); 7 return; 8 }
1 //2、判断用户是否存在 2 if (baseController.CurrentUser == null) 3 { 4 filterContext.HttpContext.Response.Write(" <script type='text/javascript'> alert('^登录已过期,请重新登录!');window.top.location='/'; </script>"); 5 filterContext.RequestContext.HttpContext.Response.End(); 6 filterContext.Result = new EmptyResult(); 7 return; 8 }
1 //对比变量,用于权限认证 2 var alias = ModuleAlias; 3 4 #region 配置Sign调取控制器标识 5 Sign = filterContext.RequestContext.HttpContext.Request.QueryString["sign"]; 6 if (!string.IsNullOrEmpty(Sign)) 7 { 8 if (("," + ModuleAlias.ToLower()).Contains("," + Sign.ToLower())) 9 { 10 alias = Sign; 11 filterContext.Controller.ViewData["Sign"] = Sign; 12 } 13 } 14 #endregion
1 //3、调用下面的方法,验证是否有访问此页面的权限,查看加操作 2 var moduleId = baseController.CurrentUser.Modules.Where(p => p.ALIAS.ToLower() == alias.ToLower()).Select(p => p.ID).FirstOrDefault(); 3 bool _blAllowed = this.IsAllowed(baseController.CurrentUser, moduleId, OperaAction); 4 if (!_blAllowed) 5 { 6 filterContext.HttpContext.Response.Write(" <script type='text/javascript'> alert('您没有访问当前页面的权限!');</script>"); 7 filterContext.RequestContext.HttpContext.Response.End(); 8 filterContext.Result = new EmptyResult(); 9 return; 10 }
1 //4、有权限访问页面,将此页面的权限集合传给页面 2 filterContext.Controller.ViewData["PermissionList"] = GetPermissByJson(baseController.CurrentUser, moduleId);
上面主要是对后台控制器方法操作权限的验证,有时候前台展示了比如 添加、删除功能,但是用户去操作的时候后台验证不通过会提示用户没有操作权限,这样显得不是很友好,我们返回个权限Json,前台按钮没有这个权限的我们就移除掉
1 /// <summary> 2 /// 获取操作权限Json字符串,供视图JS判断使用 3 /// </summary> 4 string GetPermissByJson(Account account, int moduleId) 5 { 6 //操作权限 7 var _varPerListThisModule = account.Permissions.Where(p => p.MODULEID == moduleId).Select(R => new { R.PERVALUE }).ToList(); 8 return Common.JsonConverter.Serialize(_varPerListThisModule); 9 }
1 /// <summary> 2 /// 功能描述:判断用户是否有此模块的操作权限 3 /// </summary> 4 bool IsAllowed(Account user, int moduleId, string action) 5 { 6 //判断入口 7 if (user == null || user.Id <= 0 || moduleId == 0 || string.IsNullOrEmpty(action)) return false; 8 //验证权限 9 var permission = user.Permissions.Where(p => p.MODULEID == moduleId); 10 action = action.Trim(','); 11 if (action.IndexOf(',') > 0) 12 { 13 permission = permission.Where(p => action.ToLower().Contains(p.PERVALUE.ToLower())); 14 } 15 else 16 { 17 permission = permission.Where(p => p.PERVALUE.ToLower() == action.ToLower()); 18 } 19 return permission.Any(); 20 }
模块去重
1 /// <summary> 2 /// 模型去重,非常重要 3 /// add yuangang by 2016-05-25 4 /// </summary> 5 public class ModuleDistinct : IEqualityComparer<Domain.SYS_MODULE> 6 { 7 public bool Equals(Domain.SYS_MODULE x, Domain.SYS_MODULE y) 8 { 9 return x.ID == y.ID; 10 } 11 12 public int GetHashCode(Domain.SYS_MODULE obj) 13 { 14 return obj.ToString().GetHashCode(); 15 } 16 }
后面,我们就用到这个基类,我先给大家看一下这个权限认证在后台是如何使用的,加上这一句就OK了
错误:对不起,我犯错误了,好几个项目开着,混了。大家可能都遇到了
因为我没有添加用户的测试数据,所以这里一直没发现这个错误,这里很明显是没有注入 在Service.xml
原创文章 转载请尊重劳动成果 http://yuangang.cnblogs.com