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!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM