MVC系列_权限管理之权限控制
随笔-23 文章-0 评论-114
一步一步Asp.Net MVC系列_权限管理之权限控制
在权限管理中一个很重要的就是关于权限的拦截验证问题,特别是我们在webform中的验证,比纯winform要更复杂,winform可以通过验证把按钮隐藏或者禁用的方式,但是在web中我们不能仅仅通过隐藏按钮,不显示菜单/按钮之类的手段,因为客户端的代码都是透明的,如果我们不在服务端把好关,那么权限根本就无从谈起,我们必须彻底的进行验证,每一步动作都要进行验证,客户端的每一个ajax提交都要进行验证,如果任何一个ajax 动作都做过验证了,那么至少可以保证基本的安全性了.
在纯webform中,我们通常怎么来进行权限控制呢?
一般情况下,设计基类然后,在基类写好验证方法,子类调用并验证
我们来看看启航动力的开源CMS怎么进行的验证:
1: using System;2: using System.Collections.Generic;3: using System.Text;4: using System.Web;5: using System.Web.UI.WebControls;6: using DTcms.Common;7:8: namespace DTcms.Web.UI9: {10: public class ManagePage : System.Web.UI.Page11: {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为Null37: if (Session[DTKeys.SESSION_ADMIN_INFO] != null )38: {39: return true ;40: }41: else42: {43: //检查Cookies44: 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); //修正BUG90: 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); //修正BUG109: 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: #endregion128:129: #region 枚举==============================================130:131: /// <summary>132: /// 统一管理操作枚举133: /// </summary>134: public enum ActionEnum135: {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: Delete156: }157:158: /// <summary>159: /// 属性类型枚举160: /// </summary>161: public enum AttributeEnum162: {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: CheckBox179: }180: #endregion181: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: #endregion209:210: }211: }在子类校验的时候继承ManagePage的基类,然后就这样校验:
可以看到这种是通常的设计方案,基类定义,子类校验,不过这个启航动力CMS,他完全是在基类定义枚举,控制仅仅停留在增删改查,浏览,这些,不过CMS确实这一层就可以了,而且它的设计感觉不太好,因为到处都是验证代码,每一个增删改查方法都有这些验证的代码,如果是我我就会用委托的方式绑定方法,在Page_Load里面验证权限,委托绑定增删改查方法,这样,把验证的过程大大节省,这些甚至可以设计成公共的方法,来实现.
我们今天讲解的是MVC里面的权限验证,MVC天然的Controller,Action,让我们的权限控制更加容易,而且更简洁,更清晰.
首先,先来看看,MVC里面的一个小特色设计:
这个是一个普通的model,在mvc示例项目中,他采用Attribute方式来验证,我当时看到的时候感觉耳目一新,以前看<<CLR VIA C#>> Attribute的时候,当时还在想这些东西可以干什么??仅仅是元数据描述??后来才发现,这东西太强大了,控件的设计,反射,ORM,无处不在,Attributes也让代码更加优雅.
我们可以在mvc中定义各种Attribute来让我们的代码更优雅,而且让权限更简洁,比如我们经常会设计,
xxxx页面登录后才能访问,
xxxxx页面匿名用户也能访问,
xxxx页面必须经过权限验证才能访问,
xxxxx各种类型
我们可以定义各种Attribute来描述它,这种描述也让权限控制更加优雅.
比如:我们有几个登录页面,错误显示页面等等,这些页面,可以单独设计一个Attribute标记来标识.
我们可以定义各种各样的标记来描述权限控制,
可以看到我们在这里只需要标记匿名标记,然后在权限拦截中进行处理,如果存在Anonymous标记就默认允许.
这里在公共基类中设计验证标记,子类继承,这样,我们就可以达到子类全部都需要验证处理..
接下来,我们就看看,我们的权限拦截如何处理的.
我们的权限拦截是通过继承ActionFilterAttribute,自定义拦截器,这里参考传说中弦哥的思路.
简单地说,ActionFilter就是Action过滤器,任何一个请求都像筛子一样的过滤.所以,这个Filter就是筛子,我们需要的就是在这个筛子上做权限控制,这样我们的权限不就容易得多了么?
这时候,我想到了以前的一张asp.net生命周期,一个请求过来同样经过了HttpModule等一层层,在那里做权限拦截,可能效果更好,如果webform设计,可能要尝试一下看看能不能这么做 .
1: /// <summary>2: /// 权限拦截3: /// </summary>4: [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false )]5: public class PermissionFilterAttribute : ActionFilterAttribute6: {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: else45: {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: //验证是否是匿名访问的Action9: object [] attrsAnonymous = filterContext.ActionDescriptor.GetCustomAttributes( typeof (AnonymousAttribute), true );10: //是否是Anonymous11: 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: //是否是LoginAllowView24: 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是否是匿名访问Action42: if (CheckAnonymous(filterContext))43: return true ;44: //未登录验证45: if (SessionHelper.Get( "UserID" ) == null )46: {47: return false ;48: }49: //验证当前Action是否是登录就可以访问的Action50: if (CheckLoginAllowView(filterContext))51: return true ;52:53: //下面开始用户权限验证54: var user = new UserService();55: SysCurrentUser CurrentUser = new SysCurrentUser();56: var controllerName = filterContext.RouteData.Values[ "controller" ].ToString();57: var actionName = filterContext.RouteData.Values[ "action" ].ToString();58: //如果是超级管理员,直接允许59: if (CurrentUser.UserID == ConfigSettings.GetAdminUserID())60: {61: return true ;62: }63: //如果拥有超级管理员的角色就默认全部允许64: string AdminUserRoleID = ConfigSettings.GetAdminUserRoleID().ToString();65: //检查当前角色组有没有超级角色66: if (Tools.CheckStringHasValue(CurrentUser.UserRoles, ',' , CurrentUser.UserRoles))67: {68: return true ;69: }70:71: //Action权限验证72: if (controllerName.ToLower() != "manage" ) //如果当前Action请求为具体的功能页并且不是Manage中 Index页和Welcome页73: {74: //验证75: if (!user.RoleHasOperatePermission(CurrentUser.UserRoles, controllerName, actionName)) //如果验证该操作是否拥有权限76: {77: return false ;78: }79: }80: //管理页面直接允许81: return true ;82: }可以看到我们仅仅这一个PermissionAttribute配合其他的Attribute就实现了权限的拦截控制.
当然,我们仍然需要配置那些东西?
过滤页面.....
有一些页面我们可以直接配置允许访问的页面:
1: var path = filterContext.HttpContext.Request.Path.ToLower();2: //获取当前配置保存起来的允许页面3: IList< string > allowPages = ConfigSettings.GetAllAllowPage();4: foreach ( string page in allowPages)5: {6: if (page.ToLower() == path)7: {8: IsIgnored = true ;9: break ;10: }11: }12: if (IsIgnored)13: return ;
我们在自定义配置文件中,可以定义允许访问的页面,比如:错误页之类的,可能有些人觉得,不需要,但是这个配置是在程序发布以后,仍然能够很轻松的进行配置,所以,预留一个配置页面还是蛮有必要的.
同时我们也需要配置超级管理员身份和角色....
有人觉得,这些需要么?
我觉得是需要的,因为数据库读取出来的角色,根本没办法分辨超级管理员,只能数据库动态配置,我们就预定义这样的一个超级管理员角色的身份ID,让这个超级管理角色拥有任何的功能和任何的权限,甚至读取数据库的时候,都是读取的所有模块权限,菜单权限.
这样我们的配置工作就非常简单了,特别是数据库没有大部分配置初试信息(比如菜单信息,角色信息,)的时候,我们不需要手动往数据库添加数据,直接在程序中添加就可以了,也算是一个超级管理员身份.
接下来,我们的权限控制基本上就差不多了,现在我们可以安心的写页面了,权限神马东西,跟咱关系就不大了,这样,分工不是更爽,干别人的活才是最纠结的.......
接下来,我们可以继续设计一些公共类来简化我们日常的操作,对于公共类的设计,我觉得要拿出最大的热情与态度,要知道这些是能够真正节省我们时间的东西.
只有设计好它,我们以后才会更快更爽更轻松 .
我们经常会遇到这样的代码:
1: /// <summary>2: /// 保存资料业务3: /// </summary>4: /// <param name="context"></param>5: public void SaveCompany(HttpContext context)6: {7: //用户json数据读取8: company Info= new company ();9: String CompanyStr = context.Request[ "Company" ];10: string id=context.Request[ "id" ];11: string pic = context.Request[ "pic" ];12: if (!Tools.IsValidInput( ref pic, true ) || !Tools.IsValidInput( ref id, true ))13: {14: return ;15: }16: //图片保存17: //System.IO.StreamWriter sw = new System.IO.StreamWriter(context.Server.MapPath("tzt.txt"), true);18: //sw.Write(CompanyStr);19: //sw.Close();20: //使用Newtonsoft.Json.dll组件解析json对象21:22: JObject o = JObject.Parse(CompanyStr);23: Info.Username = ( string )o.SelectToken( "Username" );24: if (! new companyBLL().CheckExistUserName(Info.Username))25: {26: context.Response.Write( false );27: return ;28: }29: Info.Password = ( string )o.SelectToken( "Password" );30: Info.Name = ( string )o.SelectToken( "Name" );31: Info.Isrecommend = (( string )o.SelectToken( "Isrecommend" ))== "true" ? "1" : "0" ;32: Info.Fac = ( string )o.SelectToken( "Fac" );33: Info.Representative = ( string )o.SelectToken( "Representative" );34: Info.Isshow = (( string )o.SelectToken( "Isshow" )) == "true" ? "1" : "0" ;35: Info.State = (( string )o.SelectToken( "State" )) == "true" ? "1" : "0" ;36: Info.State1 = (( string )o.SelectToken( "State1" )) == "true" ? "1" : "0" ;37:38: Info.Zipcode = ( string )o.SelectToken( "Zipcode" );39: Info.QQ = ( string )o.SelectToken( "QQ" );40: Info.Telephone = ( string )o.SelectToken( "Telephone" );41: Info.mobilephone = ( string )o.SelectToken( "mobilephone" );42: Info.Email = ( string )o.SelectToken( "Email" );43: Info.Address = ( string )o.SelectToken( "Address" );44: Info.Website = ( string )o.SelectToken( "Website" );45: Info.Award = ( string )o.SelectToken( "Award" );46: Info.Introduction = ( string )o.SelectToken( "Introduction" );47: Info.Picturepath = pic;48:49: if (! string .IsNullOrEmpty(id))50: Info.Id = Convert.ToInt32(id);51:52: if (!Info.Id.HasValue)53: {54: Info.rank = 0;55: Info.hit = 0;56: //执行增加操作57: new companyBLL().AddNew(Info);58: SMTP smtp = new SMTP(Info.Email);59: string webpath = context.Request.Url.Scheme + "://" + context.Request.Url.Authority + System.Web.VirtualPathUtility.ToAbsolute( "~/Default.aspx" );60: smtp.Activation(webpath, Info.Name); //发送激活邮件61: }62: else63: {64: new companyBLL().Update(Info);65: }66:67:68: }这是我以前一个电子商务网站项目写的,
我现在看,简直到处都是毛病.......
首先,关于form读取内容占了一大块.......
可以想象,到处都是赋值语句,看到都吐血,再加上一些验证非空,验证函数等等,更是罪加一等的恶劣.
当然我们需要一步一步的解决这些问题,首先是剥离关于表单读取的内容,封装到一个类中,然后在类中进行验证处理.
权限验证中的样例,
验证也没有做大块的处理,不过大家可以注意,可以看到想当的简洁,我们同时需要各种辅助类的设计,强制转化的处理,非空验证的处理,等等,我们直接设计成扩展方法就能达到上图中ObjToIntNULL()的形式, 也不需要ConvertToInt32(xxxxx)的形式来转化了,不是更简洁么?
1: /* 作者: tianzh2: * 创建时间: 2012/7/22 15:38:203: *4: */5: using System;6: using System.Collections.Generic;7: using System.Linq;8: using System.Text;9:10: namespace TZHSWEET.Common11: {12: /// <summary>13: /// 强制转化辅助类(无异常抛出)14: /// </summary>15: public static class ConvertHelper16: {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: try48: {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: /// 强制转化为long79: /// </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: /// 强制转化为string117: /// </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: try148: {149: return Convert.ToDecimal(obj);150: }151: catch152: {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: #endregion174:175: }176: }一些好的常用的扩展方法同样是一个相当大的改进,对于代码,也更清晰,更简洁,我们同时可以设计一些非空验证等等.
1: #region 判断对象是否为空2: /// <summary>3: /// 判断对象是否为空,为空返回true4: /// </summary>5: /// <typeparam name="T">要验证的对象的类型</typeparam>6: /// <param name="data">要验证的对象</param>7: public static bool IsNullOrEmpty<T>( this T data)8: {9: //如果为null10: 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: //如果为DBNull25: if (data.GetType() == typeof (DBNull))26: {27: return true ;28: }29:30: //不为空31: return false ;32: }33:34: /// <summary>35: /// 判断对象是否为空,为空返回true36: /// </summary>37: /// <param name="data">要验证的对象</param>38: public static bool IsNullOrEmpty( this object data)39: {40: //如果为null41: 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: //如果为DBNull56: if (data.GetType() == typeof (DBNull))57: {58: return true ;59: }60:61: //不为空62: return false ;63: }64: #endregion这些东西其实真没有太多难的东西,只是简单的为了追求更简洁更方便而已,磨刀不误砍柴工就是这个道理,思考怎么偷懒才是程序员应该干的事情,不管多么忙都要停下来思考,想想怎么样才能偷懒!
界面上,不太会美工,直接照搬的LigerUI界面!
目前进度已经完成的差不多了,接下来要陆续总结学到的经验与技巧!
mvc学习系列,等我写完权限管理,上传源码到网盘,呵呵! 一直在努力,从未曾放弃,努力学习中..... 欢迎一起学习.net!
分类: .net , asp.net MVC , 权限管理设计
随笔-23 文章-0 评论-114
一步一步Asp.Net MVC系列_权限管理之权限控制
在权限管理中一个很重要的就是关于权限的拦截验证问题,特别是我们在webform中的验证,比纯winform要更复杂,winform可以通过验证把按钮隐藏或者禁用的方式,但是在web中我们不能仅仅通过隐藏按钮,不显示菜单/按钮之类的手段,因为客户端的代码都是透明的,如果我们不在服务端把好关,那么权限根本就无从谈起,我们必须彻底的进行验证,每一步动作都要进行验证,客户端的每一个ajax提交都要进行验证,如果任何一个ajax 动作都做过验证了,那么至少可以保证基本的安全性了.
在纯webform中,我们通常怎么来进行权限控制呢?
一般情况下,设计基类然后,在基类写好验证方法,子类调用并验证
我们来看看启航动力的开源CMS怎么进行的验证:
1: using System;2: using System.Collections.Generic;3: using System.Text;4: using System.Web;5: using System.Web.UI.WebControls;6: using DTcms.Common;7:8: namespace DTcms.Web.UI9: {10: public class ManagePage : System.Web.UI.Page11: {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为Null37: if (Session[DTKeys.SESSION_ADMIN_INFO] != null )38: {39: return true ;40: }41: else42: {43: //检查Cookies44: 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); //修正BUG90: 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); //修正BUG109: 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: #endregion128:129: #region 枚举==============================================130:131: /// <summary>132: /// 统一管理操作枚举133: /// </summary>134: public enum ActionEnum135: {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: Delete156: }157:158: /// <summary>159: /// 属性类型枚举160: /// </summary>161: public enum AttributeEnum162: {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: CheckBox179: }180: #endregion181: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: #endregion209:210: }211: }在子类校验的时候继承ManagePage的基类,然后就这样校验:
可以看到这种是通常的设计方案,基类定义,子类校验,不过这个启航动力CMS,他完全是在基类定义枚举,控制仅仅停留在增删改查,浏览,这些,不过CMS确实这一层就可以了,而且它的设计感觉不太好,因为到处都是验证代码,每一个增删改查方法都有这些验证的代码,如果是我我就会用委托的方式绑定方法,在Page_Load里面验证权限,委托绑定增删改查方法,这样,把验证的过程大大节省,这些甚至可以设计成公共的方法,来实现.
我们今天讲解的是MVC里面的权限验证,MVC天然的Controller,Action,让我们的权限控制更加容易,而且更简洁,更清晰.
首先,先来看看,MVC里面的一个小特色设计:
这个是一个普通的model,在mvc示例项目中,他采用Attribute方式来验证,我当时看到的时候感觉耳目一新,以前看<<CLR VIA C#>> Attribute的时候,当时还在想这些东西可以干什么??仅仅是元数据描述??后来才发现,这东西太强大了,控件的设计,反射,ORM,无处不在,Attributes也让代码更加优雅.
我们可以在mvc中定义各种Attribute来让我们的代码更优雅,而且让权限更简洁,比如我们经常会设计,
xxxx页面登录后才能访问,
xxxxx页面匿名用户也能访问,
xxxx页面必须经过权限验证才能访问,
xxxxx各种类型
我们可以定义各种Attribute来描述它,这种描述也让权限控制更加优雅.
比如:我们有几个登录页面,错误显示页面等等,这些页面,可以单独设计一个Attribute标记来标识.
我们可以定义各种各样的标记来描述权限控制,
可以看到我们在这里只需要标记匿名标记,然后在权限拦截中进行处理,如果存在Anonymous标记就默认允许.
这里在公共基类中设计验证标记,子类继承,这样,我们就可以达到子类全部都需要验证处理..
接下来,我们就看看,我们的权限拦截如何处理的.
我们的权限拦截是通过继承ActionFilterAttribute,自定义拦截器,这里参考传说中弦哥的思路.
简单地说,ActionFilter就是Action过滤器,任何一个请求都像筛子一样的过滤.所以,这个Filter就是筛子,我们需要的就是在这个筛子上做权限控制,这样我们的权限不就容易得多了么?
这时候,我想到了以前的一张asp.net生命周期,一个请求过来同样经过了HttpModule等一层层,在那里做权限拦截,可能效果更好,如果webform设计,可能要尝试一下看看能不能这么做 .
1: /// <summary>2: /// 权限拦截3: /// </summary>4: [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false )]5: public class PermissionFilterAttribute : ActionFilterAttribute6: {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: else45: {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: //验证是否是匿名访问的Action9: object [] attrsAnonymous = filterContext.ActionDescriptor.GetCustomAttributes( typeof (AnonymousAttribute), true );10: //是否是Anonymous11: 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: //是否是LoginAllowView24: 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是否是匿名访问Action42: if (CheckAnonymous(filterContext))43: return true ;44: //未登录验证45: if (SessionHelper.Get( "UserID" ) == null )46: {47: return false ;48: }49: //验证当前Action是否是登录就可以访问的Action50: if (CheckLoginAllowView(filterContext))51: return true ;52:53: //下面开始用户权限验证54: var user = new UserService();55: SysCurrentUser CurrentUser = new SysCurrentUser();56: var controllerName = filterContext.RouteData.Values[ "controller" ].ToString();57: var actionName = filterContext.RouteData.Values[ "action" ].ToString();58: //如果是超级管理员,直接允许59: if (CurrentUser.UserID == ConfigSettings.GetAdminUserID())60: {61: return true ;62: }63: //如果拥有超级管理员的角色就默认全部允许64: string AdminUserRoleID = ConfigSettings.GetAdminUserRoleID().ToString();65: //检查当前角色组有没有超级角色66: if (Tools.CheckStringHasValue(CurrentUser.UserRoles, ',' , CurrentUser.UserRoles))67: {68: return true ;69: }70:71: //Action权限验证72: if (controllerName.ToLower() != "manage" ) //如果当前Action请求为具体的功能页并且不是Manage中 Index页和Welcome页73: {74: //验证75: if (!user.RoleHasOperatePermission(CurrentUser.UserRoles, controllerName, actionName)) //如果验证该操作是否拥有权限76: {77: return false ;78: }79: }80: //管理页面直接允许81: return true ;82: }可以看到我们仅仅这一个PermissionAttribute配合其他的Attribute就实现了权限的拦截控制.
当然,我们仍然需要配置那些东西?
过滤页面.....
有一些页面我们可以直接配置允许访问的页面:
1: var path = filterContext.HttpContext.Request.Path.ToLower();2: //获取当前配置保存起来的允许页面3: IList< string > allowPages = ConfigSettings.GetAllAllowPage();4: foreach ( string page in allowPages)5: {6: if (page.ToLower() == path)7: {8: IsIgnored = true ;9: break ;10: }11: }12: if (IsIgnored)13: return ;
我们在自定义配置文件中,可以定义允许访问的页面,比如:错误页之类的,可能有些人觉得,不需要,但是这个配置是在程序发布以后,仍然能够很轻松的进行配置,所以,预留一个配置页面还是蛮有必要的.
同时我们也需要配置超级管理员身份和角色....
有人觉得,这些需要么?
我觉得是需要的,因为数据库读取出来的角色,根本没办法分辨超级管理员,只能数据库动态配置,我们就预定义这样的一个超级管理员角色的身份ID,让这个超级管理角色拥有任何的功能和任何的权限,甚至读取数据库的时候,都是读取的所有模块权限,菜单权限.
这样我们的配置工作就非常简单了,特别是数据库没有大部分配置初试信息(比如菜单信息,角色信息,)的时候,我们不需要手动往数据库添加数据,直接在程序中添加就可以了,也算是一个超级管理员身份.
接下来,我们的权限控制基本上就差不多了,现在我们可以安心的写页面了,权限神马东西,跟咱关系就不大了,这样,分工不是更爽,干别人的活才是最纠结的.......
接下来,我们可以继续设计一些公共类来简化我们日常的操作,对于公共类的设计,我觉得要拿出最大的热情与态度,要知道这些是能够真正节省我们时间的东西.
只有设计好它,我们以后才会更快更爽更轻松 .
我们经常会遇到这样的代码:
1: /// <summary>2: /// 保存资料业务3: /// </summary>4: /// <param name="context"></param>5: public void SaveCompany(HttpContext context)6: {7: //用户json数据读取8: company Info= new company ();9: String CompanyStr = context.Request[ "Company" ];10: string id=context.Request[ "id" ];11: string pic = context.Request[ "pic" ];12: if (!Tools.IsValidInput( ref pic, true ) || !Tools.IsValidInput( ref id, true ))13: {14: return ;15: }16: //图片保存17: //System.IO.StreamWriter sw = new System.IO.StreamWriter(context.Server.MapPath("tzt.txt"), true);18: //sw.Write(CompanyStr);19: //sw.Close();20: //使用Newtonsoft.Json.dll组件解析json对象21:22: JObject o = JObject.Parse(CompanyStr);23: Info.Username = ( string )o.SelectToken( "Username" );24: if (! new companyBLL().CheckExistUserName(Info.Username))25: {26: context.Response.Write( false );27: return ;28: }29: Info.Password = ( string )o.SelectToken( "Password" );30: Info.Name = ( string )o.SelectToken( "Name" );31: Info.Isrecommend = (( string )o.SelectToken( "Isrecommend" ))== "true" ? "1" : "0" ;32: Info.Fac = ( string )o.SelectToken( "Fac" );33: Info.Representative = ( string )o.SelectToken( "Representative" );34: Info.Isshow = (( string )o.SelectToken( "Isshow" )) == "true" ? "1" : "0" ;35: Info.State = (( string )o.SelectToken( "State" )) == "true" ? "1" : "0" ;36: Info.State1 = (( string )o.SelectToken( "State1" )) == "true" ? "1" : "0" ;37:38: Info.Zipcode = ( string )o.SelectToken( "Zipcode" );39: Info.QQ = ( string )o.SelectToken( "QQ" );40: Info.Telephone = ( string )o.SelectToken( "Telephone" );41: Info.mobilephone = ( string )o.SelectToken( "mobilephone" );42: Info.Email = ( string )o.SelectToken( "Email" );43: Info.Address = ( string )o.SelectToken( "Address" );44: Info.Website = ( string )o.SelectToken( "Website" );45: Info.Award = ( string )o.SelectToken( "Award" );46: Info.Introduction = ( string )o.SelectToken( "Introduction" );47: Info.Picturepath = pic;48:49: if (! string .IsNullOrEmpty(id))50: Info.Id = Convert.ToInt32(id);51:52: if (!Info.Id.HasValue)53: {54: Info.rank = 0;55: Info.hit = 0;56: //执行增加操作57: new companyBLL().AddNew(Info);58: SMTP smtp = new SMTP(Info.Email);59: string webpath = context.Request.Url.Scheme + "://" + context.Request.Url.Authority + System.Web.VirtualPathUtility.ToAbsolute( "~/Default.aspx" );60: smtp.Activation(webpath, Info.Name); //发送激活邮件61: }62: else63: {64: new companyBLL().Update(Info);65: }66:67:68: }这是我以前一个电子商务网站项目写的,
我现在看,简直到处都是毛病.......
首先,关于form读取内容占了一大块.......
可以想象,到处都是赋值语句,看到都吐血,再加上一些验证非空,验证函数等等,更是罪加一等的恶劣.
当然我们需要一步一步的解决这些问题,首先是剥离关于表单读取的内容,封装到一个类中,然后在类中进行验证处理.
权限验证中的样例,
验证也没有做大块的处理,不过大家可以注意,可以看到想当的简洁,我们同时需要各种辅助类的设计,强制转化的处理,非空验证的处理,等等,我们直接设计成扩展方法就能达到上图中ObjToIntNULL()的形式, 也不需要ConvertToInt32(xxxxx)的形式来转化了,不是更简洁么?
1: /* 作者: tianzh2: * 创建时间: 2012/7/22 15:38:203: *4: */5: using System;6: using System.Collections.Generic;7: using System.Linq;8: using System.Text;9:10: namespace TZHSWEET.Common11: {12: /// <summary>13: /// 强制转化辅助类(无异常抛出)14: /// </summary>15: public static class ConvertHelper16: {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: try48: {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: /// 强制转化为long79: /// </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: /// 强制转化为string117: /// </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: try148: {149: return Convert.ToDecimal(obj);150: }151: catch152: {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: #endregion174:175: }176: }一些好的常用的扩展方法同样是一个相当大的改进,对于代码,也更清晰,更简洁,我们同时可以设计一些非空验证等等.
1: #region 判断对象是否为空2: /// <summary>3: /// 判断对象是否为空,为空返回true4: /// </summary>5: /// <typeparam name="T">要验证的对象的类型</typeparam>6: /// <param name="data">要验证的对象</param>7: public static bool IsNullOrEmpty<T>( this T data)8: {9: //如果为null10: 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: //如果为DBNull25: if (data.GetType() == typeof (DBNull))26: {27: return true ;28: }29:30: //不为空31: return false ;32: }33:34: /// <summary>35: /// 判断对象是否为空,为空返回true36: /// </summary>37: /// <param name="data">要验证的对象</param>38: public static bool IsNullOrEmpty( this object data)39: {40: //如果为null41: 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: //如果为DBNull56: if (data.GetType() == typeof (DBNull))57: {58: return true ;59: }60:61: //不为空62: return false ;63: }64: #endregion这些东西其实真没有太多难的东西,只是简单的为了追求更简洁更方便而已,磨刀不误砍柴工就是这个道理,思考怎么偷懒才是程序员应该干的事情,不管多么忙都要停下来思考,想想怎么样才能偷懒!
界面上,不太会美工,直接照搬的LigerUI界面!
目前进度已经完成的差不多了,接下来要陆续总结学到的经验与技巧!
mvc学习系列,等我写完权限管理,上传源码到网盘,呵呵! 一直在努力,从未曾放弃,努力学习中..... 欢迎一起学习.net!
分类: .net , asp.net MVC , 权限管理设计
作者: Leo_wl
出处: http://www.cnblogs.com/Leo_wl/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
版权信息声明:本文来自网络,不代表【好得很程序员自学网】立场,转载请注明出处:http://www.haodehen.cn/did48314