好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

MVC系列_权限管理之权限控制

MVC系列_权限管理之权限控制

随笔-23  文章-0  评论-114 

一步一步Asp.Net MVC系列_权限管理之权限控制

 

在权限管理中一个很重要的就是关于权限的拦截验证问题,特别是我们在webform中的验证,比纯winform要更复杂,winform可以通过验证把按钮隐藏或者禁用的方式,但是在web中我们不能仅仅通过隐藏按钮,不显示菜单/按钮之类的手段,因为客户端的代码都是透明的,如果我们不在服务端把好关,那么权限根本就无从谈起,我们必须彻底的进行验证,每一步动作都要进行验证,客户端的每一个ajax提交都要进行验证,如果任何一个ajax 动作都做过验证了,那么至少可以保证基本的安全性了.

在纯webform中,我们通常怎么来进行权限控制呢?

一般情况下,设计基类然后,在基类写好验证方法,子类调用并验证

我们来看看启航动力的开源CMS怎么进行的验证:

    1:    using  System;
    2:    using  System.Collections.Generic;
    3:    using  System.Text;
    4:    using  System.Web;
    5:    using  System.Web.UI.WebControls;
    6:    using  DTcms.Common;
    7:    
    8:    namespace  DTcms.Web.UI
    9:   {
   10:        public   class  ManagePage : System.Web.UI.Page
   11:       {
   12:            protected   internal  Model.siteconfig siteConfig;
   13:    
   14:            public  ManagePage()
   15:           {
   16:                this .Load +=  new  EventHandler(ManagePage_Load);
   17:               siteConfig =  new  BLL.siteconfig().loadConfig(Utils.GetXmlMapPath( "Configpath" ));
   18:           }
   19:    
   20:            private   void  ManagePage_Load( object  sender, EventArgs e)
   21:           {
   22:                //判断管理员是否登录 
   23:                if  (!IsAdminLogin())
   24:               {
   25:                   Response.Write( "<script>parent.location.href='"  + siteConfig.webpath + siteConfig.webmanagepath +  "/login.aspx'</script>" );
   26:                   Response.End();
   27:               }
   28:           }
   29:    
   30:            #region  管理员============================================
   31:            /// <summary> 
   32:            /// 判断管理员是否已经登录(解决Session超时问题) 
   33:            /// </summary> 
   34:            public   bool  IsAdminLogin()
   35:           {
   36:                //如果Session为Null 
   37:                if  (Session[DTKeys.SESSION_ADMIN_INFO] !=  null )
   38:               {
   39:                    return   true ;
   40:               }
   41:                else 
   42:               {
   43:                    //检查Cookies 
   44:                    string  adminname = Utils.GetCookie( "AdminName" ,  "DTcms" );  //解密用户名 
   45:                    string  adminpwd = Utils.GetCookie( "AdminPwd" ,  "DTcms" );
   46:                    if  (adminname !=  ""  && adminpwd !=  "" )
   47:                   {
   48:                       BLL.manager bll =  new  BLL.manager();
   49:                       Model.manager model = bll.GetModel(adminname, adminpwd);
   50:                        if  (model !=  null )
   51:                       {
   52:                           Session[DTKeys.SESSION_ADMIN_INFO] = model;
   53:                            return   true ;
   54:                       }
   55:                   }
   56:               }
   57:                return   false ;
   58:           }
   59:    
   60:            /// <summary> 
   61:            /// 取得管理员信息 
   62:            /// </summary> 
   63:            public  Model.manager GetAdminInfo()
   64:           {
   65:                if  (IsAdminLogin())
   66:               {
   67:                   Model.manager model = Session[DTKeys.SESSION_ADMIN_INFO]  as  Model.manager;
   68:                    if  (model !=  null )
   69:                   {
   70:                        return  model;
   71:                   }
   72:               }
   73:                return   null ;
   74:           }
   75:    
   76:            /// <summary> 
   77:            /// 检查管理员权限 
   78:            /// </summary> 
   79:            /// <param name="channel_id">频道ID</param> 
   80:            /// <param name="action_type">操作类型</param> 
   81:            public   void  ChkAdminLevel( int  channel_id,  string  action_type)
   82:           {
   83:               Model.manager model = GetAdminInfo();
   84:               BLL.manager_role bll =  new  BLL.manager_role();
   85:                bool  result = bll.Exists(model.role_id, channel_id, action_type);
   86:                if  (!result)
   87:               {
   88:                    string  msbox =  "parent.f_errorTab(\"错误提示\", \"您没有管理该页面的权限,请勿尝试非法进入!\")" ;
   89:                    //ClientScript.RegisterClientScriptBlock(Page.GetType(), "JsPrint", msbox.ToString(), true);  //修正BUG 
   90:                   Response.Write( "<script type=\"text/javascript\">"  + msbox +  "</script>" );
   91:                   Response.End();
   92:               }
   93:           }
   94:    
   95:            /// <summary> 
   96:            /// 检查管理员权限 
   97:            /// </summary> 
   98:            /// <param name="channel_name">栏目名称</param> 
   99:            /// <param name="action_type">操作类型</param> 
  100:            public   void  ChkAdminLevel( string  channel_name,  string  action_type)
  101:           {
  102:               Model.manager model = GetAdminInfo();
  103:               BLL.manager_role bll =  new  BLL.manager_role();
  104:                bool  result = bll.Exists(model.role_id, channel_name, action_type);
  105:                if  (!result)
  106:               {
  107:                    string  msbox =  "parent.f_errorTab(\"错误提示\", \"您没有管理该页面的权限,请勿尝试非法进入!\")" ;
  108:                    //ClientScript.RegisterClientScriptBlock(Page.GetType(), "JsPrint", msbox.ToString(), true);  //修正BUG 
  109:                   Response.Write( "<script type=\"text/javascript\">"  + msbox +  "</script>" );
  110:                   Response.End();
  111:               }
  112:           }
  113:    
  114:            /// <summary> 
  115:            /// 检查管理员权限 
  116:            /// </summary> 
  117:            /// <param name="channel_name">栏目名称</param> 
  118:            /// <param name="action_type">操作类型</param> 
  119:            /// <returns>bool</returns> 
  120:            public   bool  IsAdminLevel( string  channel_name,  string  action_type)
  121:           {
  122:               Model.manager model = GetAdminInfo();
  123:               BLL.manager_role bll =  new  BLL.manager_role();
  124:                return  bll.Exists(model.role_id, channel_name, action_type);
  125:           }
  126:    
  127:            #endregion 
  128:    
  129:            #region  枚举==============================================
  130:    
  131:            /// <summary> 
  132:            /// 统一管理操作枚举 
  133:            /// </summary> 
  134:            public   enum  ActionEnum
  135:           {
  136:                /// <summary> 
  137:                /// 所有 
  138:                /// </summary> 
  139:               All,
  140:                /// <summary> 
  141:                /// 查看 
  142:                /// </summary> 
  143:               View,
  144:                /// <summary> 
  145:                /// 添加 
  146:                /// </summary> 
  147:               Add,
  148:                /// <summary> 
  149:                /// 修改 
  150:                /// </summary> 
  151:               Edit,
  152:                /// <summary> 
  153:                /// 删除 
  154:                /// </summary> 
  155:               Delete
  156:           }
  157:    
  158:            /// <summary> 
  159:            /// 属性类型枚举 
  160:            /// </summary> 
  161:            public   enum  AttributeEnum
  162:           {
  163:                /// <summary> 
  164:                /// 输入框 
  165:                /// </summary> 
  166:               Text,
  167:                /// <summary> 
  168:                /// 下拉框 
  169:                /// </summary> 
  170:               Select,
  171:                /// <summary> 
  172:                /// 单选框 
  173:                /// </summary> 
  174:               Radio,
  175:                /// <summary> 
  176:                /// 复选框 
  177:                /// </summary> 
  178:               CheckBox
  179:           }
  180:            #endregion 
  181:    
  182:            #region  JS提示============================================
  183:    
  184:            /// <summary> 
  185:            /// 添加编辑删除提示 
  186:            /// </summary> 
  187:            /// <param name="msgtitle">提示文字</param> 
  188:            /// <param name="url">返回地址</param> 
  189:            /// <param name="msgcss">CSS样式</param> 
  190:            protected   void  JscriptMsg( string  msgtitle,  string  url,  string  msgcss)
  191:           {
  192:                string  msbox =  "parent.jsprint(\""  + msgtitle +  "\", \""  + url +  "\", \""  + msgcss +  "\")" ;
  193:               ClientScript.RegisterClientScriptBlock(Page.GetType(),  "JsPrint" , msbox,  true );
  194:           }
  195:    
  196:            /// <summary> 
  197:            /// 带回传函数的添加编辑删除提示 
  198:            /// </summary> 
  199:            /// <param name="msgtitle">提示文字</param> 
  200:            /// <param name="url">返回地址</param> 
  201:            /// <param name="msgcss">CSS样式</param> 
  202:            /// <param name="callback">JS回调函数</param> 
  203:            protected   void  JscriptMsg( string  msgtitle,  string  url,  string  msgcss,  string  callback)
  204:           {
  205:                string  msbox =  "parent.jsprint(\""  + msgtitle +  "\", \""  + url +  "\", \""  + msgcss +  "\", "  + callback +  ")" ;
  206:               ClientScript.RegisterClientScriptBlock(Page.GetType(),  "JsPrint" , msbox,  true );
  207:           }
  208:            #endregion 
  209:    
  210:       }
  211:   }

在子类校验的时候继承ManagePage的基类,然后就这样校验:

可以看到这种是通常的设计方案,基类定义,子类校验,不过这个启航动力CMS,他完全是在基类定义枚举,控制仅仅停留在增删改查,浏览,这些,不过CMS确实这一层就可以了,而且它的设计感觉不太好,因为到处都是验证代码,每一个增删改查方法都有这些验证的代码,如果是我我就会用委托的方式绑定方法,在Page_Load里面验证权限,委托绑定增删改查方法,这样,把验证的过程大大节省,这些甚至可以设计成公共的方法,来实现.

我们今天讲解的是MVC里面的权限验证,MVC天然的Controller,Action,让我们的权限控制更加容易,而且更简洁,更清晰.

首先,先来看看,MVC里面的一个小特色设计:

这个是一个普通的model,在mvc示例项目中,他采用Attribute方式来验证,我当时看到的时候感觉耳目一新,以前看<<CLR VIA C#>> Attribute的时候,当时还在想这些东西可以干什么??仅仅是元数据描述??后来才发现,这东西太强大了,控件的设计,反射,ORM,无处不在,Attributes也让代码更加优雅.

我们可以在mvc中定义各种Attribute来让我们的代码更优雅,而且让权限更简洁,比如我们经常会设计,

  xxxx页面登录后才能访问,

  xxxxx页面匿名用户也能访问,

  xxxx页面必须经过权限验证才能访问,

  xxxxx各种类型

我们可以定义各种Attribute来描述它,这种描述也让权限控制更加优雅.

比如:我们有几个登录页面,错误显示页面等等,这些页面,可以单独设计一个Attribute标记来标识.

我们可以定义各种各样的标记来描述权限控制,

可以看到我们在这里只需要标记匿名标记,然后在权限拦截中进行处理,如果存在Anonymous标记就默认允许.

这里在公共基类中设计验证标记,子类继承,这样,我们就可以达到子类全部都需要验证处理..

接下来,我们就看看,我们的权限拦截如何处理的.

我们的权限拦截是通过继承ActionFilterAttribute,自定义拦截器,这里参考传说中弦哥的思路.

简单地说,ActionFilter就是Action过滤器,任何一个请求都像筛子一样的过滤.所以,这个Filter就是筛子,我们需要的就是在这个筛子上做权限控制,这样我们的权限不就容易得多了么?

这时候,我想到了以前的一张asp.net生命周期,一个请求过来同样经过了HttpModule等一层层,在那里做权限拦截,可能效果更好,如果webform设计,可能要尝试一下看看能不能这么做 .

    1:      /// <summary> 
    2:        /// 权限拦截 
    3:        /// </summary> 
    4:       [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple =  false )]
    5:        public   class  PermissionFilterAttribute : ActionFilterAttribute
    6:       {
    7:            /// <summary> 
    8:            /// 权限拦截 
    9:            /// </summary> 
   10:            /// <param name="filterContext"></param> 
   11:            public   override   void  OnActionExecuting(ActionExecutingContext filterContext)
   12:           {
   13:                //权限拦截是否忽略 
   14:                bool  IsIgnored =  false ;
   15:                if  (filterContext ==  null )
   16:               {
   17:                    throw   new  ArgumentNullException( "filterContext" );
   18:               }
   19:               var path = filterContext.HttpContext.Request.Path.ToLower();
   20:                //获取当前配置保存起来的允许页面 
   21:               IList< string > allowPages = ConfigSettings.GetAllAllowPage();
   22:                foreach  ( string  page  in  allowPages)
   23:               {
   24:                    if  (page.ToLower() == path)
   25:                   {
   26:                       IsIgnored =  true ;
   27:                        break ;
   28:                   }
   29:               }
   30:                if  (IsIgnored)
   31:                    return ;
   32:                //接下来进行权限拦截与验证 
   33:                object [] attrs = filterContext.ActionDescriptor.GetCustomAttributes( typeof (ViewPageAttribute),  true );
   34:               var isViewPage = attrs.Length == 1; //当前Action请求是否为具体的功能页 
   35:    
   36:                if  ( this .AuthorizeCore(filterContext) ==  false ) //根据验证判断进行处理 
   37:               {
   38:                    //注:如果未登录直接在URL输入功能权限地址提示不是很友好;如果登录后输入未维护的功能权限地址,那么也可以访问,这个可能会有安全问题 
   39:                    if  (isViewPage ==  true )
   40:                   {
   41:                        //跳转到登录页面 
   42:                       filterContext.RequestContext.HttpContext.Response.Redirect( "~/Admin/Manage/UserLogin" );
   43:                   }
   44:                    else 
   45:                   {
   46:    
   47:                        //跳转到登录页面 
   48:                       filterContext.RequestContext.HttpContext.Response.Redirect( "~/Admin/Manage/Error" );
   49:                   }
   50:               }
   51:           }

这个Attribute直接借鉴的弦哥的模型,

这样我们可以对程序进行细化的处理过程.

针对不同的标记进行处理:

    1:      /// <summary> 
    2:            /// [Anonymous标记]验证是否匿名访问 
    3:            /// </summary> 
    4:            /// <param name="filterContext"></param> 
    5:            /// <returns></returns> 
    6:            public   bool  CheckAnonymous(ActionExecutingContext filterContext)
    7:           {
    8:                //验证是否是匿名访问的Action 
    9:                object [] attrsAnonymous = filterContext.ActionDescriptor.GetCustomAttributes( typeof (AnonymousAttribute),  true );
   10:                //是否是Anonymous 
   11:               var Anonymous = attrsAnonymous.Length == 1;
   12:                return  Anonymous;
   13:           }
   14:            /// <summary> 
   15:            /// [LoginAllowView标记]验证是否登录就可以访问(如果已经登陆,那么不对于标识了LoginAllowView的方法就不需要验证了) 
   16:            /// </summary> 
   17:            /// <param name="filterContext"></param> 
   18:            /// <returns></returns> 
   19:            public   bool  CheckLoginAllowView(ActionExecutingContext filterContext)
   20:           {
   21:                //在这里允许一种情况,如果已经登陆,那么不对于标识了LoginAllowView的方法就不需要验证了 
   22:                object [] attrs = filterContext.ActionDescriptor.GetCustomAttributes( typeof (LoginAllowViewAttribute),  true );
   23:                //是否是LoginAllowView 
   24:               var ViewMethod = attrs.Length == 1;
   25:                return  ViewMethod;
   26:           }
   27:    
   28:            /// <summary> 
   29:            /// //权限判断业务逻辑 
   30:            /// </summary> 
   31:            /// <param name="filterContext"></param> 
   32:            /// <param name="isViewPage">是否是页面</param> 
   33:            /// <returns></returns> 
   34:            protected   virtual   bool  AuthorizeCore(ActionExecutingContext filterContext)
   35:           {
   36:    
   37:                if  (filterContext.HttpContext ==  null )
   38:               {
   39:                    throw   new  ArgumentNullException( "httpContext" );
   40:               }
   41:                //验证当前Action是否是匿名访问Action 
   42:                if  (CheckAnonymous(filterContext))
   43:                    return   true ;
   44:                //未登录验证 
   45:                if  (SessionHelper.Get( "UserID" ) ==  null )
   46:               {
   47:                    return   false ;
   48:               }
   49:                //验证当前Action是否是登录就可以访问的Action 
   50:                if  (CheckLoginAllowView(filterContext))
   51:                    return   true ;
   52:    
   53:                //下面开始用户权限验证 
   54:               var user =  new  UserService();
   55:               SysCurrentUser CurrentUser =  new  SysCurrentUser();
   56:               var controllerName = filterContext.RouteData.Values[ "controller" ].ToString();
   57:               var actionName = filterContext.RouteData.Values[ "action" ].ToString();
   58:                //如果是超级管理员,直接允许 
   59:                if  (CurrentUser.UserID == ConfigSettings.GetAdminUserID())
   60:               {
   61:                    return   true ;
   62:               }
   63:                //如果拥有超级管理员的角色就默认全部允许 
   64:                string  AdminUserRoleID = ConfigSettings.GetAdminUserRoleID().ToString();
   65:                //检查当前角色组有没有超级角色 
   66:                if  (Tools.CheckStringHasValue(CurrentUser.UserRoles,  ',' , CurrentUser.UserRoles))
   67:               {
   68:                    return   true ;
   69:               }
   70:    
   71:                //Action权限验证 
   72:                if  (controllerName.ToLower() !=  "manage" ) //如果当前Action请求为具体的功能页并且不是Manage中 Index页和Welcome页 
   73:               {
   74:                    //验证 
   75:                    if  (!user.RoleHasOperatePermission(CurrentUser.UserRoles, controllerName, actionName)) //如果验证该操作是否拥有权限 
   76:                   {
   77:                        return   false ;
   78:                   }
   79:               }
   80:                //管理页面直接允许 
   81:                return   true ;
   82:           }

可以看到我们仅仅这一个PermissionAttribute配合其他的Attribute就实现了权限的拦截控制.

当然,我们仍然需要配置那些东西?

过滤页面.....

有一些页面我们可以直接配置允许访问的页面:

    1:     var path = filterContext.HttpContext.Request.Path.ToLower();
    2:                //获取当前配置保存起来的允许页面 
    3:               IList< string > allowPages = ConfigSettings.GetAllAllowPage();
    4:                foreach  ( string  page  in  allowPages)
    5:               {
    6:                    if  (page.ToLower() == path)
    7:                   {
    8:                       IsIgnored =  true ;
    9:                        break ;
   10:                   }
   11:               }
   12:                if  (IsIgnored)
   13:                    return ;

我们在自定义配置文件中,可以定义允许访问的页面,比如:错误页之类的,可能有些人觉得,不需要,但是这个配置是在程序发布以后,仍然能够很轻松的进行配置,所以,预留一个配置页面还是蛮有必要的.

同时我们也需要配置超级管理员身份和角色....

有人觉得,这些需要么?

我觉得是需要的,因为数据库读取出来的角色,根本没办法分辨超级管理员,只能数据库动态配置,我们就预定义这样的一个超级管理员角色的身份ID,让这个超级管理角色拥有任何的功能和任何的权限,甚至读取数据库的时候,都是读取的所有模块权限,菜单权限.

这样我们的配置工作就非常简单了,特别是数据库没有大部分配置初试信息(比如菜单信息,角色信息,)的时候,我们不需要手动往数据库添加数据,直接在程序中添加就可以了,也算是一个超级管理员身份.

接下来,我们的权限控制基本上就差不多了,现在我们可以安心的写页面了,权限神马东西,跟咱关系就不大了,这样,分工不是更爽,干别人的活才是最纠结的.......

接下来,我们可以继续设计一些公共类来简化我们日常的操作,对于公共类的设计,我觉得要拿出最大的热情与态度,要知道这些是能够真正节省我们时间的东西.

只有设计好它,我们以后才会更快更爽更轻松 .

我们经常会遇到这样的代码:

    1:      /// <summary> 
    2:        /// 保存资料业务 
    3:        /// </summary> 
    4:        /// <param name="context"></param> 
    5:        public   void  SaveCompany(HttpContext context)
    6:       {
    7:            //用户json数据读取 
    8:           company Info= new  company ();
    9:           String CompanyStr = context.Request[ "Company" ];
   10:            string  id=context.Request[ "id" ];
   11:            string  pic = context.Request[ "pic" ];
   12:            if  (!Tools.IsValidInput( ref  pic,  true ) || !Tools.IsValidInput( ref  id,  true ))
   13:           {
   14:                return ;
   15:           }
   16:          //图片保存 
   17:            //System.IO.StreamWriter sw = new System.IO.StreamWriter(context.Server.MapPath("tzt.txt"), true); 
   18:            //sw.Write(CompanyStr); 
   19:            //sw.Close(); 
   20:            //使用Newtonsoft.Json.dll组件解析json对象 
   21:        
   22:           JObject o = JObject.Parse(CompanyStr);
   23:           Info.Username = ( string )o.SelectToken( "Username" );
   24:            if  (! new  companyBLL().CheckExistUserName(Info.Username))
   25:           {
   26:               context.Response.Write( false );
   27:                return ;
   28:           }
   29:           Info.Password = ( string )o.SelectToken( "Password" );
   30:           Info.Name = ( string )o.SelectToken( "Name" );
   31:           Info.Isrecommend = (( string )o.SelectToken( "Isrecommend" ))== "true"  ?  "1"  :  "0" ;
   32:           Info.Fac = ( string )o.SelectToken( "Fac" );
   33:           Info.Representative = ( string )o.SelectToken( "Representative" );
   34:           Info.Isshow = (( string )o.SelectToken( "Isshow" )) ==  "true"  ?  "1"  :  "0" ;
   35:           Info.State = (( string )o.SelectToken( "State" )) ==  "true"  ?  "1"  :  "0" ;
   36:           Info.State1 = (( string )o.SelectToken( "State1" )) ==  "true"  ?  "1"  :  "0" ;
   37:          
   38:           Info.Zipcode = ( string )o.SelectToken( "Zipcode" );
   39:           Info.QQ = ( string )o.SelectToken( "QQ" );
   40:           Info.Telephone = ( string )o.SelectToken( "Telephone" );
   41:           Info.mobilephone = ( string )o.SelectToken( "mobilephone" );
   42:           Info.Email = ( string )o.SelectToken( "Email" );
   43:           Info.Address = ( string )o.SelectToken( "Address" );
   44:           Info.Website = ( string )o.SelectToken( "Website" );
   45:           Info.Award = ( string )o.SelectToken( "Award" );
   46:           Info.Introduction = ( string )o.SelectToken( "Introduction" );
   47:           Info.Picturepath = pic;
   48:    
   49:            if  (! string .IsNullOrEmpty(id))
   50:               Info.Id = Convert.ToInt32(id);
   51:    
   52:            if  (!Info.Id.HasValue)
   53:           {
   54:               Info.rank = 0;
   55:               Info.hit = 0;
   56:                //执行增加操作 
   57:                new  companyBLL().AddNew(Info);
   58:               SMTP smtp =  new  SMTP(Info.Email);
   59:                string  webpath = context.Request.Url.Scheme +  "://"  + context.Request.Url.Authority + System.Web.VirtualPathUtility.ToAbsolute( "~/Default.aspx" );
   60:               smtp.Activation(webpath, Info.Name); //发送激活邮件 
   61:           }
   62:            else 
   63:           {
   64:                new  companyBLL().Update(Info);
   65:           }
   66:    
   67:    
   68:       }

这是我以前一个电子商务网站项目写的,

我现在看,简直到处都是毛病.......

首先,关于form读取内容占了一大块.......

可以想象,到处都是赋值语句,看到都吐血,再加上一些验证非空,验证函数等等,更是罪加一等的恶劣.

当然我们需要一步一步的解决这些问题,首先是剥离关于表单读取的内容,封装到一个类中,然后在类中进行验证处理.

权限验证中的样例,

验证也没有做大块的处理,不过大家可以注意,可以看到想当的简洁,我们同时需要各种辅助类的设计,强制转化的处理,非空验证的处理,等等,我们直接设计成扩展方法就能达到上图中ObjToIntNULL()的形式, 也不需要ConvertToInt32(xxxxx)的形式来转化了,不是更简洁么?

    1:    /*  作者:       tianzh 
    2:    *  创建时间:   2012/7/22 15:38:20 
    3:    * 
    4:    */ 
    5:    using  System;
    6:    using  System.Collections.Generic;
    7:    using  System.Linq;
    8:    using  System.Text;
    9:    
   10:    namespace  TZHSWEET.Common
   11:   {
   12:        /// <summary> 
   13:        /// 强制转化辅助类(无异常抛出) 
   14:        /// </summary> 
   15:        public   static   class  ConvertHelper
   16:       {
   17:            #region  强制转化
   18:            /// <summary> 
   19:            /// object转化为Bool类型 
   20:            /// </summary> 
   21:            /// <param name="obj"></param> 
   22:            /// <returns></returns> 
   23:            public   static   bool  ObjToBool( this   object  obj)
   24:           {
   25:                bool  flag;
   26:                if  (obj ==  null )
   27:               {
   28:                    return   false ;
   29:               }
   30:                if  (obj.Equals(DBNull.Value))
   31:               {
   32:                    return   false ;
   33:               }
   34:                return  ( bool .TryParse(obj.ToString(),  out  flag) && flag);
   35:           }
   36:            /// <summary> 
   37:            /// object强制转化为DateTime类型(吃掉异常) 
   38:            /// </summary> 
   39:            /// <param name="obj"></param> 
   40:            /// <returns></returns> 
   41:            public   static  DateTime? ObjToDateNull( this   object  obj)
   42:           {
   43:                if  (obj ==  null )
   44:               {
   45:                    return   null ;
   46:               }
   47:                try 
   48:               {
   49:                    return   new  DateTime?(Convert.ToDateTime(obj));
   50:               }
   51:                catch  (ArgumentNullException ex)
   52:               {
   53:                    return   null ;
   54:               }
   55:           }
   56:            /// <summary> 
   57:            /// int强制转化 
   58:            /// </summary> 
   59:            /// <param name="obj"></param> 
   60:            /// <returns></returns> 
   61:            public   static   int  ObjToInt( this   object  obj)
   62:           {
   63:                if  (obj !=  null )
   64:               {
   65:                    int  num;
   66:                    if  (obj.Equals(DBNull.Value))
   67:                   {
   68:                        return  0;
   69:                   }
   70:                    if  ( int .TryParse(obj.ToString(),  out  num))
   71:                   {
   72:                        return  num;
   73:                   }
   74:               }
   75:                return  0;
   76:           }
   77:            /// <summary> 
   78:            /// 强制转化为long 
   79:            /// </summary> 
   80:            /// <param name="obj"></param> 
   81:            /// <returns></returns> 
   82:            public   static   long  ObjToLong( this   object  obj)
   83:           {
   84:                if  (obj !=  null )
   85:               {
   86:                    long  num;
   87:                    if  (obj.Equals(DBNull.Value))
   88:                   {
   89:                        return  0;
   90:                   }
   91:                    if  ( long .TryParse(obj.ToString(),  out  num))
   92:                   {
   93:                        return  num;
   94:                   }
   95:               }
   96:                return  0;
   97:           }
   98:            /// <summary> 
   99:            /// 强制转化可空int类型 
  100:            /// </summary> 
  101:            /// <param name="obj"></param> 
  102:            /// <returns></returns> 
  103:            public   static   int ? ObjToIntNull( this   object  obj)
  104:           {
  105:                if  (obj ==  null )
  106:               {
  107:                    return   null ;
  108:               }
  109:                if  (obj.Equals(DBNull.Value))
  110:               {
  111:                    return   null ;
  112:               }
  113:                return   new   int ?(ObjToInt(obj));
  114:           }
  115:            /// <summary> 
  116:            /// 强制转化为string 
  117:            /// </summary> 
  118:            /// <param name="obj"></param> 
  119:            /// <returns></returns> 
  120:            public   static   string  ObjToStr( this   object  obj)
  121:           {
  122:                if  (obj ==  null )
  123:               {
  124:                    return   "" ;
  125:               }
  126:                if  (obj.Equals(DBNull.Value))
  127:               {
  128:                    return   "" ;
  129:               }
  130:                return  Convert.ToString(obj);
  131:           }
  132:            /// <summary> 
  133:            /// Decimal转化 
  134:            /// </summary> 
  135:            /// <param name="obj"></param> 
  136:            /// <returns></returns> 
  137:            public   static   decimal  ObjToDecimal( this   object  obj)
  138:           {
  139:                if  (obj ==  null )
  140:               {
  141:                    return  0M;
  142:               }
  143:                if  (obj.Equals(DBNull.Value))
  144:               {
  145:                    return  0M;
  146:               }
  147:                try 
  148:               {
  149:                    return  Convert.ToDecimal(obj);
  150:               }
  151:                catch 
  152:               {
  153:                    return  0M;
  154:               }
  155:           }
  156:            /// <summary> 
  157:            /// Decimal可空类型转化 
  158:            /// </summary> 
  159:            /// <param name="obj"></param> 
  160:            /// <returns></returns> 
  161:            public   static   decimal ? ObjToDecimalNull( this   object  obj)
  162:           {
  163:                if  (obj ==  null )
  164:               {
  165:                    return   null ;
  166:               }
  167:                if  (obj.Equals(DBNull.Value))
  168:               {
  169:                    return   null ;
  170:               }
  171:                return   new   decimal ?(ObjToDecimal(obj));
  172:           } 
  173:            #endregion 
  174:    
  175:       }
  176:   }

一些好的常用的扩展方法同样是一个相当大的改进,对于代码,也更清晰,更简洁,我们同时可以设计一些非空验证等等.

    1:      #region  判断对象是否为空
    2:            /// <summary> 
    3:            /// 判断对象是否为空,为空返回true 
    4:            /// </summary> 
    5:            /// <typeparam name="T">要验证的对象的类型</typeparam> 
    6:            /// <param name="data">要验证的对象</param>         
    7:            public   static   bool  IsNullOrEmpty<T>( this  T data)
    8:           {
    9:                //如果为null 
   10:                if  (data ==  null )
   11:               {
   12:                    return   true ;
   13:               }
   14:    
   15:                //如果为"" 
   16:                if  (data.GetType() ==  typeof (String))
   17:               {
   18:                    if  ( string .IsNullOrEmpty(data.ToString().Trim())||data.ToString ()== "" )
   19:                   {
   20:                        return   true ;
   21:                   }
   22:               }
   23:    
   24:                //如果为DBNull 
   25:                if  (data.GetType() ==  typeof (DBNull))
   26:               {
   27:                    return   true ;
   28:               }
   29:    
   30:                //不为空 
   31:                return   false ;
   32:           }
   33:    
   34:            /// <summary> 
   35:            /// 判断对象是否为空,为空返回true 
   36:            /// </summary> 
   37:            /// <param name="data">要验证的对象</param> 
   38:            public   static   bool  IsNullOrEmpty( this   object  data)
   39:           {
   40:                //如果为null 
   41:                if  (data ==  null )
   42:               {
   43:                    return   true ;
   44:               }
   45:    
   46:                //如果为"" 
   47:                if  (data.GetType() ==  typeof (String))
   48:               {
   49:                    if  ( string .IsNullOrEmpty(data.ToString().Trim()))
   50:                   {
   51:                        return   true ;
   52:                   }
   53:               }
   54:    
   55:                //如果为DBNull 
   56:                if  (data.GetType() ==  typeof (DBNull))
   57:               {
   58:                    return   true ;
   59:               }
   60:    
   61:                //不为空 
   62:                return   false ;
   63:           }
   64:            #endregion 

这些东西其实真没有太多难的东西,只是简单的为了追求更简洁更方便而已,磨刀不误砍柴工就是这个道理,思考怎么偷懒才是程序员应该干的事情,不管多么忙都要停下来思考,想想怎么样才能偷懒!

界面上,不太会美工,直接照搬的LigerUI界面!

目前进度已经完成的差不多了,接下来要陆续总结学到的经验与技巧!

mvc学习系列,等我写完权限管理,上传源码到网盘,呵呵! 一直在努力,从未曾放弃,努力学习中..... 欢迎一起学习.net!

分类:  .net ,  asp.net MVC ,  权限管理设计

随笔-23  文章-0  评论-114 

一步一步Asp.Net MVC系列_权限管理之权限控制

 

在权限管理中一个很重要的就是关于权限的拦截验证问题,特别是我们在webform中的验证,比纯winform要更复杂,winform可以通过验证把按钮隐藏或者禁用的方式,但是在web中我们不能仅仅通过隐藏按钮,不显示菜单/按钮之类的手段,因为客户端的代码都是透明的,如果我们不在服务端把好关,那么权限根本就无从谈起,我们必须彻底的进行验证,每一步动作都要进行验证,客户端的每一个ajax提交都要进行验证,如果任何一个ajax 动作都做过验证了,那么至少可以保证基本的安全性了.

在纯webform中,我们通常怎么来进行权限控制呢?

一般情况下,设计基类然后,在基类写好验证方法,子类调用并验证

我们来看看启航动力的开源CMS怎么进行的验证:

    1:    using  System;
    2:    using  System.Collections.Generic;
    3:    using  System.Text;
    4:    using  System.Web;
    5:    using  System.Web.UI.WebControls;
    6:    using  DTcms.Common;
    7:    
    8:    namespace  DTcms.Web.UI
    9:   {
   10:        public   class  ManagePage : System.Web.UI.Page
   11:       {
   12:            protected   internal  Model.siteconfig siteConfig;
   13:    
   14:            public  ManagePage()
   15:           {
   16:                this .Load +=  new  EventHandler(ManagePage_Load);
   17:               siteConfig =  new  BLL.siteconfig().loadConfig(Utils.GetXmlMapPath( "Configpath" ));
   18:           }
   19:    
   20:            private   void  ManagePage_Load( object  sender, EventArgs e)
   21:           {
   22:                //判断管理员是否登录 
   23:                if  (!IsAdminLogin())
   24:               {
   25:                   Response.Write( "<script>parent.location.href='"  + siteConfig.webpath + siteConfig.webmanagepath +  "/login.aspx'</script>" );
   26:                   Response.End();
   27:               }
   28:           }
   29:    
   30:            #region  管理员============================================
   31:            /// <summary> 
   32:            /// 判断管理员是否已经登录(解决Session超时问题) 
   33:            /// </summary> 
   34:            public   bool  IsAdminLogin()
   35:           {
   36:                //如果Session为Null 
   37:                if  (Session[DTKeys.SESSION_ADMIN_INFO] !=  null )
   38:               {
   39:                    return   true ;
   40:               }
   41:                else 
   42:               {
   43:                    //检查Cookies 
   44:                    string  adminname = Utils.GetCookie( "AdminName" ,  "DTcms" );  //解密用户名 
   45:                    string  adminpwd = Utils.GetCookie( "AdminPwd" ,  "DTcms" );
   46:                    if  (adminname !=  ""  && adminpwd !=  "" )
   47:                   {
   48:                       BLL.manager bll =  new  BLL.manager();
   49:                       Model.manager model = bll.GetModel(adminname, adminpwd);
   50:                        if  (model !=  null )
   51:                       {
   52:                           Session[DTKeys.SESSION_ADMIN_INFO] = model;
   53:                            return   true ;
   54:                       }
   55:                   }
   56:               }
   57:                return   false ;
   58:           }
   59:    
   60:            /// <summary> 
   61:            /// 取得管理员信息 
   62:            /// </summary> 
   63:            public  Model.manager GetAdminInfo()
   64:           {
   65:                if  (IsAdminLogin())
   66:               {
   67:                   Model.manager model = Session[DTKeys.SESSION_ADMIN_INFO]  as  Model.manager;
   68:                    if  (model !=  null )
   69:                   {
   70:                        return  model;
   71:                   }
   72:               }
   73:                return   null ;
   74:           }
   75:    
   76:            /// <summary> 
   77:            /// 检查管理员权限 
   78:            /// </summary> 
   79:            /// <param name="channel_id">频道ID</param> 
   80:            /// <param name="action_type">操作类型</param> 
   81:            public   void  ChkAdminLevel( int  channel_id,  string  action_type)
   82:           {
   83:               Model.manager model = GetAdminInfo();
   84:               BLL.manager_role bll =  new  BLL.manager_role();
   85:                bool  result = bll.Exists(model.role_id, channel_id, action_type);
   86:                if  (!result)
   87:               {
   88:                    string  msbox =  "parent.f_errorTab(\"错误提示\", \"您没有管理该页面的权限,请勿尝试非法进入!\")" ;
   89:                    //ClientScript.RegisterClientScriptBlock(Page.GetType(), "JsPrint", msbox.ToString(), true);  //修正BUG 
   90:                   Response.Write( "<script type=\"text/javascript\">"  + msbox +  "</script>" );
   91:                   Response.End();
   92:               }
   93:           }
   94:    
   95:            /// <summary> 
   96:            /// 检查管理员权限 
   97:            /// </summary> 
   98:            /// <param name="channel_name">栏目名称</param> 
   99:            /// <param name="action_type">操作类型</param> 
  100:            public   void  ChkAdminLevel( string  channel_name,  string  action_type)
  101:           {
  102:               Model.manager model = GetAdminInfo();
  103:               BLL.manager_role bll =  new  BLL.manager_role();
  104:                bool  result = bll.Exists(model.role_id, channel_name, action_type);
  105:                if  (!result)
  106:               {
  107:                    string  msbox =  "parent.f_errorTab(\"错误提示\", \"您没有管理该页面的权限,请勿尝试非法进入!\")" ;
  108:                    //ClientScript.RegisterClientScriptBlock(Page.GetType(), "JsPrint", msbox.ToString(), true);  //修正BUG 
  109:                   Response.Write( "<script type=\"text/javascript\">"  + msbox +  "</script>" );
  110:                   Response.End();
  111:               }
  112:           }
  113:    
  114:            /// <summary> 
  115:            /// 检查管理员权限 
  116:            /// </summary> 
  117:            /// <param name="channel_name">栏目名称</param> 
  118:            /// <param name="action_type">操作类型</param> 
  119:            /// <returns>bool</returns> 
  120:            public   bool  IsAdminLevel( string  channel_name,  string  action_type)
  121:           {
  122:               Model.manager model = GetAdminInfo();
  123:               BLL.manager_role bll =  new  BLL.manager_role();
  124:                return  bll.Exists(model.role_id, channel_name, action_type);
  125:           }
  126:    
  127:            #endregion 
  128:    
  129:            #region  枚举==============================================
  130:    
  131:            /// <summary> 
  132:            /// 统一管理操作枚举 
  133:            /// </summary> 
  134:            public   enum  ActionEnum
  135:           {
  136:                /// <summary> 
  137:                /// 所有 
  138:                /// </summary> 
  139:               All,
  140:                /// <summary> 
  141:                /// 查看 
  142:                /// </summary> 
  143:               View,
  144:                /// <summary> 
  145:                /// 添加 
  146:                /// </summary> 
  147:               Add,
  148:                /// <summary> 
  149:                /// 修改 
  150:                /// </summary> 
  151:               Edit,
  152:                /// <summary> 
  153:                /// 删除 
  154:                /// </summary> 
  155:               Delete
  156:           }
  157:    
  158:            /// <summary> 
  159:            /// 属性类型枚举 
  160:            /// </summary> 
  161:            public   enum  AttributeEnum
  162:           {
  163:                /// <summary> 
  164:                /// 输入框 
  165:                /// </summary> 
  166:               Text,
  167:                /// <summary> 
  168:                /// 下拉框 
  169:                /// </summary> 
  170:               Select,
  171:                /// <summary> 
  172:                /// 单选框 
  173:                /// </summary> 
  174:               Radio,
  175:                /// <summary> 
  176:                /// 复选框 
  177:                /// </summary> 
  178:               CheckBox
  179:           }
  180:            #endregion 
  181:    
  182:            #region  JS提示============================================
  183:    
  184:            /// <summary> 
  185:            /// 添加编辑删除提示 
  186:            /// </summary> 
  187:            /// <param name="msgtitle">提示文字</param> 
  188:            /// <param name="url">返回地址</param> 
  189:            /// <param name="msgcss">CSS样式</param> 
  190:            protected   void  JscriptMsg( string  msgtitle,  string  url,  string  msgcss)
  191:           {
  192:                string  msbox =  "parent.jsprint(\""  + msgtitle +  "\", \""  + url +  "\", \""  + msgcss +  "\")" ;
  193:               ClientScript.RegisterClientScriptBlock(Page.GetType(),  "JsPrint" , msbox,  true );
  194:           }
  195:    
  196:            /// <summary> 
  197:            /// 带回传函数的添加编辑删除提示 
  198:            /// </summary> 
  199:            /// <param name="msgtitle">提示文字</param> 
  200:            /// <param name="url">返回地址</param> 
  201:            /// <param name="msgcss">CSS样式</param> 
  202:            /// <param name="callback">JS回调函数</param> 
  203:            protected   void  JscriptMsg( string  msgtitle,  string  url,  string  msgcss,  string  callback)
  204:           {
  205:                string  msbox =  "parent.jsprint(\""  + msgtitle +  "\", \""  + url +  "\", \""  + msgcss +  "\", "  + callback +  ")" ;
  206:               ClientScript.RegisterClientScriptBlock(Page.GetType(),  "JsPrint" , msbox,  true );
  207:           }
  208:            #endregion 
  209:    
  210:       }
  211:   }

在子类校验的时候继承ManagePage的基类,然后就这样校验:

可以看到这种是通常的设计方案,基类定义,子类校验,不过这个启航动力CMS,他完全是在基类定义枚举,控制仅仅停留在增删改查,浏览,这些,不过CMS确实这一层就可以了,而且它的设计感觉不太好,因为到处都是验证代码,每一个增删改查方法都有这些验证的代码,如果是我我就会用委托的方式绑定方法,在Page_Load里面验证权限,委托绑定增删改查方法,这样,把验证的过程大大节省,这些甚至可以设计成公共的方法,来实现.

我们今天讲解的是MVC里面的权限验证,MVC天然的Controller,Action,让我们的权限控制更加容易,而且更简洁,更清晰.

首先,先来看看,MVC里面的一个小特色设计:

这个是一个普通的model,在mvc示例项目中,他采用Attribute方式来验证,我当时看到的时候感觉耳目一新,以前看<<CLR VIA C#>> Attribute的时候,当时还在想这些东西可以干什么??仅仅是元数据描述??后来才发现,这东西太强大了,控件的设计,反射,ORM,无处不在,Attributes也让代码更加优雅.

我们可以在mvc中定义各种Attribute来让我们的代码更优雅,而且让权限更简洁,比如我们经常会设计,

  xxxx页面登录后才能访问,

  xxxxx页面匿名用户也能访问,

  xxxx页面必须经过权限验证才能访问,

  xxxxx各种类型

我们可以定义各种Attribute来描述它,这种描述也让权限控制更加优雅.

比如:我们有几个登录页面,错误显示页面等等,这些页面,可以单独设计一个Attribute标记来标识.

我们可以定义各种各样的标记来描述权限控制,

可以看到我们在这里只需要标记匿名标记,然后在权限拦截中进行处理,如果存在Anonymous标记就默认允许.

这里在公共基类中设计验证标记,子类继承,这样,我们就可以达到子类全部都需要验证处理..

接下来,我们就看看,我们的权限拦截如何处理的.

我们的权限拦截是通过继承ActionFilterAttribute,自定义拦截器,这里参考传说中弦哥的思路.

简单地说,ActionFilter就是Action过滤器,任何一个请求都像筛子一样的过滤.所以,这个Filter就是筛子,我们需要的就是在这个筛子上做权限控制,这样我们的权限不就容易得多了么?

这时候,我想到了以前的一张asp.net生命周期,一个请求过来同样经过了HttpModule等一层层,在那里做权限拦截,可能效果更好,如果webform设计,可能要尝试一下看看能不能这么做 .

    1:      /// <summary> 
    2:        /// 权限拦截 
    3:        /// </summary> 
    4:       [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple =  false )]
    5:        public   class  PermissionFilterAttribute : ActionFilterAttribute
    6:       {
    7:            /// <summary> 
    8:            /// 权限拦截 
    9:            /// </summary> 
   10:            /// <param name="filterContext"></param> 
   11:            public   override   void  OnActionExecuting(ActionExecutingContext filterContext)
   12:           {
   13:                //权限拦截是否忽略 
   14:                bool  IsIgnored =  false ;
   15:                if  (filterContext ==  null )
   16:               {
   17:                    throw   new  ArgumentNullException( "filterContext" );
   18:               }
   19:               var path = filterContext.HttpContext.Request.Path.ToLower();
   20:                //获取当前配置保存起来的允许页面 
   21:               IList< string > allowPages = ConfigSettings.GetAllAllowPage();
   22:                foreach  ( string  page  in  allowPages)
   23:               {
   24:                    if  (page.ToLower() == path)
   25:                   {
   26:                       IsIgnored =  true ;
   27:                        break ;
   28:                   }
   29:               }
   30:                if  (IsIgnored)
   31:                    return ;
   32:                //接下来进行权限拦截与验证 
   33:                object [] attrs = filterContext.ActionDescriptor.GetCustomAttributes( typeof (ViewPageAttribute),  true );
   34:               var isViewPage = attrs.Length == 1; //当前Action请求是否为具体的功能页 
   35:    
   36:                if  ( this .AuthorizeCore(filterContext) ==  false ) //根据验证判断进行处理 
   37:               {
   38:                    //注:如果未登录直接在URL输入功能权限地址提示不是很友好;如果登录后输入未维护的功能权限地址,那么也可以访问,这个可能会有安全问题 
   39:                    if  (isViewPage ==  true )
   40:                   {
   41:                        //跳转到登录页面 
   42:                       filterContext.RequestContext.HttpContext.Response.Redirect( "~/Admin/Manage/UserLogin" );
   43:                   }
   44:                    else 
   45:                   {
   46:    
   47:                        //跳转到登录页面 
   48:                       filterContext.RequestContext.HttpContext.Response.Redirect( "~/Admin/Manage/Error" );
   49:                   }
   50:               }
   51:           }

这个Attribute直接借鉴的弦哥的模型,

这样我们可以对程序进行细化的处理过程.

针对不同的标记进行处理:

    1:      /// <summary> 
    2:            /// [Anonymous标记]验证是否匿名访问 
    3:            /// </summary> 
    4:            /// <param name="filterContext"></param> 
    5:            /// <returns></returns> 
    6:            public   bool  CheckAnonymous(ActionExecutingContext filterContext)
    7:           {
    8:                //验证是否是匿名访问的Action 
    9:                object [] attrsAnonymous = filterContext.ActionDescriptor.GetCustomAttributes( typeof (AnonymousAttribute),  true );
   10:                //是否是Anonymous 
   11:               var Anonymous = attrsAnonymous.Length == 1;
   12:                return  Anonymous;
   13:           }
   14:            /// <summary> 
   15:            /// [LoginAllowView标记]验证是否登录就可以访问(如果已经登陆,那么不对于标识了LoginAllowView的方法就不需要验证了) 
   16:            /// </summary> 
   17:            /// <param name="filterContext"></param> 
   18:            /// <returns></returns> 
   19:            public   bool  CheckLoginAllowView(ActionExecutingContext filterContext)
   20:           {
   21:                //在这里允许一种情况,如果已经登陆,那么不对于标识了LoginAllowView的方法就不需要验证了 
   22:                object [] attrs = filterContext.ActionDescriptor.GetCustomAttributes( typeof (LoginAllowViewAttribute),  true );
   23:                //是否是LoginAllowView 
   24:               var ViewMethod = attrs.Length == 1;
   25:                return  ViewMethod;
   26:           }
   27:    
   28:            /// <summary> 
   29:            /// //权限判断业务逻辑 
   30:            /// </summary> 
   31:            /// <param name="filterContext"></param> 
   32:            /// <param name="isViewPage">是否是页面</param> 
   33:            /// <returns></returns> 
   34:            protected   virtual   bool  AuthorizeCore(ActionExecutingContext filterContext)
   35:           {
   36:    
   37:                if  (filterContext.HttpContext ==  null )
   38:               {
   39:                    throw   new  ArgumentNullException( "httpContext" );
   40:               }
   41:                //验证当前Action是否是匿名访问Action 
   42:                if  (CheckAnonymous(filterContext))
   43:                    return   true ;
   44:                //未登录验证 
   45:                if  (SessionHelper.Get( "UserID" ) ==  null )
   46:               {
   47:                    return   false ;
   48:               }
   49:                //验证当前Action是否是登录就可以访问的Action 
   50:                if  (CheckLoginAllowView(filterContext))
   51:                    return   true ;
   52:    
   53:                //下面开始用户权限验证 
   54:               var user =  new  UserService();
   55:               SysCurrentUser CurrentUser =  new  SysCurrentUser();
   56:               var controllerName = filterContext.RouteData.Values[ "controller" ].ToString();
   57:               var actionName = filterContext.RouteData.Values[ "action" ].ToString();
   58:                //如果是超级管理员,直接允许 
   59:                if  (CurrentUser.UserID == ConfigSettings.GetAdminUserID())
   60:               {
   61:                    return   true ;
   62:               }
   63:                //如果拥有超级管理员的角色就默认全部允许 
   64:                string  AdminUserRoleID = ConfigSettings.GetAdminUserRoleID().ToString();
   65:                //检查当前角色组有没有超级角色 
   66:                if  (Tools.CheckStringHasValue(CurrentUser.UserRoles,  ',' , CurrentUser.UserRoles))
   67:               {
   68:                    return   true ;
   69:               }
   70:    
   71:                //Action权限验证 
   72:                if  (controllerName.ToLower() !=  "manage" ) //如果当前Action请求为具体的功能页并且不是Manage中 Index页和Welcome页 
   73:               {
   74:                    //验证 
   75:                    if  (!user.RoleHasOperatePermission(CurrentUser.UserRoles, controllerName, actionName)) //如果验证该操作是否拥有权限 
   76:                   {
   77:                        return   false ;
   78:                   }
   79:               }
   80:                //管理页面直接允许 
   81:                return   true ;
   82:           }

可以看到我们仅仅这一个PermissionAttribute配合其他的Attribute就实现了权限的拦截控制.

当然,我们仍然需要配置那些东西?

过滤页面.....

有一些页面我们可以直接配置允许访问的页面:

    1:     var path = filterContext.HttpContext.Request.Path.ToLower();
    2:                //获取当前配置保存起来的允许页面 
    3:               IList< string > allowPages = ConfigSettings.GetAllAllowPage();
    4:                foreach  ( string  page  in  allowPages)
    5:               {
    6:                    if  (page.ToLower() == path)
    7:                   {
    8:                       IsIgnored =  true ;
    9:                        break ;
   10:                   }
   11:               }
   12:                if  (IsIgnored)
   13:                    return ;

我们在自定义配置文件中,可以定义允许访问的页面,比如:错误页之类的,可能有些人觉得,不需要,但是这个配置是在程序发布以后,仍然能够很轻松的进行配置,所以,预留一个配置页面还是蛮有必要的.

同时我们也需要配置超级管理员身份和角色....

有人觉得,这些需要么?

我觉得是需要的,因为数据库读取出来的角色,根本没办法分辨超级管理员,只能数据库动态配置,我们就预定义这样的一个超级管理员角色的身份ID,让这个超级管理角色拥有任何的功能和任何的权限,甚至读取数据库的时候,都是读取的所有模块权限,菜单权限.

这样我们的配置工作就非常简单了,特别是数据库没有大部分配置初试信息(比如菜单信息,角色信息,)的时候,我们不需要手动往数据库添加数据,直接在程序中添加就可以了,也算是一个超级管理员身份.

接下来,我们的权限控制基本上就差不多了,现在我们可以安心的写页面了,权限神马东西,跟咱关系就不大了,这样,分工不是更爽,干别人的活才是最纠结的.......

接下来,我们可以继续设计一些公共类来简化我们日常的操作,对于公共类的设计,我觉得要拿出最大的热情与态度,要知道这些是能够真正节省我们时间的东西.

只有设计好它,我们以后才会更快更爽更轻松 .

我们经常会遇到这样的代码:

    1:      /// <summary> 
    2:        /// 保存资料业务 
    3:        /// </summary> 
    4:        /// <param name="context"></param> 
    5:        public   void  SaveCompany(HttpContext context)
    6:       {
    7:            //用户json数据读取 
    8:           company Info= new  company ();
    9:           String CompanyStr = context.Request[ "Company" ];
   10:            string  id=context.Request[ "id" ];
   11:            string  pic = context.Request[ "pic" ];
   12:            if  (!Tools.IsValidInput( ref  pic,  true ) || !Tools.IsValidInput( ref  id,  true ))
   13:           {
   14:                return ;
   15:           }
   16:          //图片保存 
   17:            //System.IO.StreamWriter sw = new System.IO.StreamWriter(context.Server.MapPath("tzt.txt"), true); 
   18:            //sw.Write(CompanyStr); 
   19:            //sw.Close(); 
   20:            //使用Newtonsoft.Json.dll组件解析json对象 
   21:        
   22:           JObject o = JObject.Parse(CompanyStr);
   23:           Info.Username = ( string )o.SelectToken( "Username" );
   24:            if  (! new  companyBLL().CheckExistUserName(Info.Username))
   25:           {
   26:               context.Response.Write( false );
   27:                return ;
   28:           }
   29:           Info.Password = ( string )o.SelectToken( "Password" );
   30:           Info.Name = ( string )o.SelectToken( "Name" );
   31:           Info.Isrecommend = (( string )o.SelectToken( "Isrecommend" ))== "true"  ?  "1"  :  "0" ;
   32:           Info.Fac = ( string )o.SelectToken( "Fac" );
   33:           Info.Representative = ( string )o.SelectToken( "Representative" );
   34:           Info.Isshow = (( string )o.SelectToken( "Isshow" )) ==  "true"  ?  "1"  :  "0" ;
   35:           Info.State = (( string )o.SelectToken( "State" )) ==  "true"  ?  "1"  :  "0" ;
   36:           Info.State1 = (( string )o.SelectToken( "State1" )) ==  "true"  ?  "1"  :  "0" ;
   37:          
   38:           Info.Zipcode = ( string )o.SelectToken( "Zipcode" );
   39:           Info.QQ = ( string )o.SelectToken( "QQ" );
   40:           Info.Telephone = ( string )o.SelectToken( "Telephone" );
   41:           Info.mobilephone = ( string )o.SelectToken( "mobilephone" );
   42:           Info.Email = ( string )o.SelectToken( "Email" );
   43:           Info.Address = ( string )o.SelectToken( "Address" );
   44:           Info.Website = ( string )o.SelectToken( "Website" );
   45:           Info.Award = ( string )o.SelectToken( "Award" );
   46:           Info.Introduction = ( string )o.SelectToken( "Introduction" );
   47:           Info.Picturepath = pic;
   48:    
   49:            if  (! string .IsNullOrEmpty(id))
   50:               Info.Id = Convert.ToInt32(id);
   51:    
   52:            if  (!Info.Id.HasValue)
   53:           {
   54:               Info.rank = 0;
   55:               Info.hit = 0;
   56:                //执行增加操作 
   57:                new  companyBLL().AddNew(Info);
   58:               SMTP smtp =  new  SMTP(Info.Email);
   59:                string  webpath = context.Request.Url.Scheme +  "://"  + context.Request.Url.Authority + System.Web.VirtualPathUtility.ToAbsolute( "~/Default.aspx" );
   60:               smtp.Activation(webpath, Info.Name); //发送激活邮件 
   61:           }
   62:            else 
   63:           {
   64:                new  companyBLL().Update(Info);
   65:           }
   66:    
   67:    
   68:       }

这是我以前一个电子商务网站项目写的,

我现在看,简直到处都是毛病.......

首先,关于form读取内容占了一大块.......

可以想象,到处都是赋值语句,看到都吐血,再加上一些验证非空,验证函数等等,更是罪加一等的恶劣.

当然我们需要一步一步的解决这些问题,首先是剥离关于表单读取的内容,封装到一个类中,然后在类中进行验证处理.

权限验证中的样例,

验证也没有做大块的处理,不过大家可以注意,可以看到想当的简洁,我们同时需要各种辅助类的设计,强制转化的处理,非空验证的处理,等等,我们直接设计成扩展方法就能达到上图中ObjToIntNULL()的形式, 也不需要ConvertToInt32(xxxxx)的形式来转化了,不是更简洁么?

    1:    /*  作者:       tianzh 
    2:    *  创建时间:   2012/7/22 15:38:20 
    3:    * 
    4:    */ 
    5:    using  System;
    6:    using  System.Collections.Generic;
    7:    using  System.Linq;
    8:    using  System.Text;
    9:    
   10:    namespace  TZHSWEET.Common
   11:   {
   12:        /// <summary> 
   13:        /// 强制转化辅助类(无异常抛出) 
   14:        /// </summary> 
   15:        public   static   class  ConvertHelper
   16:       {
   17:            #region  强制转化
   18:            /// <summary> 
   19:            /// object转化为Bool类型 
   20:            /// </summary> 
   21:            /// <param name="obj"></param> 
   22:            /// <returns></returns> 
   23:            public   static   bool  ObjToBool( this   object  obj)
   24:           {
   25:                bool  flag;
   26:                if  (obj ==  null )
   27:               {
   28:                    return   false ;
   29:               }
   30:                if  (obj.Equals(DBNull.Value))
   31:               {
   32:                    return   false ;
   33:               }
   34:                return  ( bool .TryParse(obj.ToString(),  out  flag) && flag);
   35:           }
   36:            /// <summary> 
   37:            /// object强制转化为DateTime类型(吃掉异常) 
   38:            /// </summary> 
   39:            /// <param name="obj"></param> 
   40:            /// <returns></returns> 
   41:            public   static  DateTime? ObjToDateNull( this   object  obj)
   42:           {
   43:                if  (obj ==  null )
   44:               {
   45:                    return   null ;
   46:               }
   47:                try 
   48:               {
   49:                    return   new  DateTime?(Convert.ToDateTime(obj));
   50:               }
   51:                catch  (ArgumentNullException ex)
   52:               {
   53:                    return   null ;
   54:               }
   55:           }
   56:            /// <summary> 
   57:            /// int强制转化 
   58:            /// </summary> 
   59:            /// <param name="obj"></param> 
   60:            /// <returns></returns> 
   61:            public   static   int  ObjToInt( this   object  obj)
   62:           {
   63:                if  (obj !=  null )
   64:               {
   65:                    int  num;
   66:                    if  (obj.Equals(DBNull.Value))
   67:                   {
   68:                        return  0;
   69:                   }
   70:                    if  ( int .TryParse(obj.ToString(),  out  num))
   71:                   {
   72:                        return  num;
   73:                   }
   74:               }
   75:                return  0;
   76:           }
   77:            /// <summary> 
   78:            /// 强制转化为long 
   79:            /// </summary> 
   80:            /// <param name="obj"></param> 
   81:            /// <returns></returns> 
   82:            public   static   long  ObjToLong( this   object  obj)
   83:           {
   84:                if  (obj !=  null )
   85:               {
   86:                    long  num;
   87:                    if  (obj.Equals(DBNull.Value))
   88:                   {
   89:                        return  0;
   90:                   }
   91:                    if  ( long .TryParse(obj.ToString(),  out  num))
   92:                   {
   93:                        return  num;
   94:                   }
   95:               }
   96:                return  0;
   97:           }
   98:            /// <summary> 
   99:            /// 强制转化可空int类型 
  100:            /// </summary> 
  101:            /// <param name="obj"></param> 
  102:            /// <returns></returns> 
  103:            public   static   int ? ObjToIntNull( this   object  obj)
  104:           {
  105:                if  (obj ==  null )
  106:               {
  107:                    return   null ;
  108:               }
  109:                if  (obj.Equals(DBNull.Value))
  110:               {
  111:                    return   null ;
  112:               }
  113:                return   new   int ?(ObjToInt(obj));
  114:           }
  115:            /// <summary> 
  116:            /// 强制转化为string 
  117:            /// </summary> 
  118:            /// <param name="obj"></param> 
  119:            /// <returns></returns> 
  120:            public   static   string  ObjToStr( this   object  obj)
  121:           {
  122:                if  (obj ==  null )
  123:               {
  124:                    return   "" ;
  125:               }
  126:                if  (obj.Equals(DBNull.Value))
  127:               {
  128:                    return   "" ;
  129:               }
  130:                return  Convert.ToString(obj);
  131:           }
  132:            /// <summary> 
  133:            /// Decimal转化 
  134:            /// </summary> 
  135:            /// <param name="obj"></param> 
  136:            /// <returns></returns> 
  137:            public   static   decimal  ObjToDecimal( this   object  obj)
  138:           {
  139:                if  (obj ==  null )
  140:               {
  141:                    return  0M;
  142:               }
  143:                if  (obj.Equals(DBNull.Value))
  144:               {
  145:                    return  0M;
  146:               }
  147:                try 
  148:               {
  149:                    return  Convert.ToDecimal(obj);
  150:               }
  151:                catch 
  152:               {
  153:                    return  0M;
  154:               }
  155:           }
  156:            /// <summary> 
  157:            /// Decimal可空类型转化 
  158:            /// </summary> 
  159:            /// <param name="obj"></param> 
  160:            /// <returns></returns> 
  161:            public   static   decimal ? ObjToDecimalNull( this   object  obj)
  162:           {
  163:                if  (obj ==  null )
  164:               {
  165:                    return   null ;
  166:               }
  167:                if  (obj.Equals(DBNull.Value))
  168:               {
  169:                    return   null ;
  170:               }
  171:                return   new   decimal ?(ObjToDecimal(obj));
  172:           } 
  173:            #endregion 
  174:    
  175:       }
  176:   }

一些好的常用的扩展方法同样是一个相当大的改进,对于代码,也更清晰,更简洁,我们同时可以设计一些非空验证等等.

    1:      #region  判断对象是否为空
    2:            /// <summary> 
    3:            /// 判断对象是否为空,为空返回true 
    4:            /// </summary> 
    5:            /// <typeparam name="T">要验证的对象的类型</typeparam> 
    6:            /// <param name="data">要验证的对象</param>         
    7:            public   static   bool  IsNullOrEmpty<T>( this  T data)
    8:           {
    9:                //如果为null 
   10:                if  (data ==  null )
   11:               {
   12:                    return   true ;
   13:               }
   14:    
   15:                //如果为"" 
   16:                if  (data.GetType() ==  typeof (String))
   17:               {
   18:                    if  ( string .IsNullOrEmpty(data.ToString().Trim())||data.ToString ()== "" )
   19:                   {
   20:                        return   true ;
   21:                   }
   22:               }
   23:    
   24:                //如果为DBNull 
   25:                if  (data.GetType() ==  typeof (DBNull))
   26:               {
   27:                    return   true ;
   28:               }
   29:    
   30:                //不为空 
   31:                return   false ;
   32:           }
   33:    
   34:            /// <summary> 
   35:            /// 判断对象是否为空,为空返回true 
   36:            /// </summary> 
   37:            /// <param name="data">要验证的对象</param> 
   38:            public   static   bool  IsNullOrEmpty( this   object  data)
   39:           {
   40:                //如果为null 
   41:                if  (data ==  null )
   42:               {
   43:                    return   true ;
   44:               }
   45:    
   46:                //如果为"" 
   47:                if  (data.GetType() ==  typeof (String))
   48:               {
   49:                    if  ( string .IsNullOrEmpty(data.ToString().Trim()))
   50:                   {
   51:                        return   true ;
   52:                   }
   53:               }
   54:    
   55:                //如果为DBNull 
   56:                if  (data.GetType() ==  typeof (DBNull))
   57:               {
   58:                    return   true ;
   59:               }
   60:    
   61:                //不为空 
   62:                return   false ;
   63:           }
   64:            #endregion 

这些东西其实真没有太多难的东西,只是简单的为了追求更简洁更方便而已,磨刀不误砍柴工就是这个道理,思考怎么偷懒才是程序员应该干的事情,不管多么忙都要停下来思考,想想怎么样才能偷懒!

界面上,不太会美工,直接照搬的LigerUI界面!

目前进度已经完成的差不多了,接下来要陆续总结学到的经验与技巧!

mvc学习系列,等我写完权限管理,上传源码到网盘,呵呵! 一直在努力,从未曾放弃,努力学习中..... 欢迎一起学习.net!

分类:  .net ,  asp.net MVC ,  权限管理设计

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于MVC系列_权限管理之权限控制的详细内容...

  阅读:46次