zoukankan      html  css  js  c++  java
  • 【无私分享:从入门到精通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>
    View Code

    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>
    View Code

    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>
    View Code

    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>
    View Code

    一、我们在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

  • 相关阅读:
    CAS无锁模式
    Redis 字符串结构和常用命令
    Redis 链表结构 和 常用命令
    Redis hash结构 和常用命令
    Spring + Redis ( 简单使用)
    使用idea 搭建一个 SpringBoot + Mybatis + logback 的maven 项
    Java 208道面试题及部分答案
    面试相关知识点
    使用IDEA 搭建一个SpringBoot + Hibernate + Gradle
    Java相关知识点
  • 原文地址:https://www.cnblogs.com/yuangang/p/5524255.html
Copyright © 2011-2022 走看看