zoukankan      html  css  js  c++  java
  • MVC系列_权限管理之权限控制

    一步一步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的基类,然后就这样校验:

    image

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

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

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

    image

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

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

      xxxx页面登录后才能访问,

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

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

      xxxxx各种类型

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

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

    image

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

    image

    image

    image

    image

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

    image

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

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

    我们的权限拦截是通过继承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;

    image

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

    image

    image

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

    有人觉得,这些需要么?

    我觉得是需要的,因为数据库读取出来的角色,根本没办法分辨超级管理员,只能数据库动态配置,我们就预定义这样的一个超级管理员角色的身份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读取内容占了一大块.......

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

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

    权限验证中的样例,

    image

    image

    验证也没有做大块的处理,不过大家可以注意,可以看到想当的简洁,我们同时需要各种辅助类的设计,强制转化的处理,非空验证的处理,等等,我们直接设计成扩展方法就能达到上图中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

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

    image

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

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

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

    一步一步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的基类,然后就这样校验:

    image

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

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

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

    image

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

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

      xxxx页面登录后才能访问,

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

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

      xxxxx各种类型

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

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

    image

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

    image

    image

    image

    image

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

    image

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

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

    我们的权限拦截是通过继承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;

    image

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

    image

    image

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

    有人觉得,这些需要么?

    我觉得是需要的,因为数据库读取出来的角色,根本没办法分辨超级管理员,只能数据库动态配置,我们就预定义这样的一个超级管理员角色的身份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读取内容占了一大块.......

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

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

    权限验证中的样例,

    image

    image

    验证也没有做大块的处理,不过大家可以注意,可以看到想当的简洁,我们同时需要各种辅助类的设计,强制转化的处理,非空验证的处理,等等,我们直接设计成扩展方法就能达到上图中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

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

    image

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

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

    mvc学习系列,等我写完权限管理,上传源码到网盘,呵呵! 一直在努力,从未曾放弃,努力学习中..... 欢迎一起学习.net!
  • 相关阅读:
    隐式类型转换
    STL::allocator rebind
    Proxy Class(代理类)
    C++ 没有合适的默认构造函数(无参数构造函数)
    E
    C
    Multiplication Puzzle POJ
    Brackets POJ
    Halloween Costumes LightOJ
    ACwing 139. 回文子串的最大长度(二分+Hash)
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/2623709.html
Copyright © 2011-2022 走看看