当前位置首页 > 法律/法规/法学 > 理论/案例
搜柄,搜必应! 快速导航 | 使用教程  [会员中心]

mvc权限控制案例

文档格式:DOC| 20 页|大小 696.01KB|积分 10|2021-11-23 发布|文档ID:41781525
第1页
下载文档到电脑,查找使用更方便 还剩页未读,继续阅读>>
1 / 20
此文档下载收益归作者所有 下载文档
  • 版权提示
  • 文本预览
  • 常见问题
  • A MVC权限设计思考 (一)数据库建库部分 目前各类的权限设计已经困扰了我们好久,对于MVC,下面我将通过ActionFilter来扩展我们的权限认证,以下示例是从我的一个课程中心项目中提取出来,希望对各位初学者起到抛砖引玉的作用下面首先来设计我们的权限控制的数据库层下面我来依次介绍每个字段的说明RoleGroup 权限组表 该表主要对系统权限进行分组,我们的用户可以直接赋予该分组,拥有所有该组权限RoleID 权限组ID 例:01RoleName 权限组名称 例:系统管理员RoleState 组状态 (是否启用) 例:TrueRoleGroupAppList 表 组权限对应表 该表主要复制每个权限组对应的权限详细列表ID ID主键 例:1RoleID 对应权限组的ID 例:01SysAppID 对应的详细权限组ID 例:01StartTime 该权限使用的起始日期 例:2009-01-01 ,该字段属性默认为:all ,即不限制起始日期EndTime 该权限使用的结束日期 例:2009-01-01 ,该字段属性默认为:all ,即不限制结束日期SysAppCate 表 该表主要是对SysAppList详细权限表做分类,当我们的权限页面特别多的时候,该表主要为了方便管理。

    可省略SysAppCateID 大类ID 例:01SysAppCateName 大类名称 例:新闻管理SysAppCateEName 大类英文名SysAppList 权限详细表 该表主要负责所有权限的基础列表SysAppID 权限ID 例:01SysAppCateID 权限大类ID 例:01SysAppName 权限名称 例:新闻添加SysAppEName 权限英文名称SysAppController 权限对应的Controller 例:NewsSysAppAction 权限对应的Action 例:Add 本权限设计针对对Action权限限制IsView 是否为可见 例:True 该字段的设计主要是便于后台的管理和设置,因为有部分Action是没有View层的,比如Post,但是在后台权限管理中我们又要用到他好了,建库部分就到这里了,下一篇我会介绍逻辑部分,其中会涉及到Repository模式,缓存,自定义的AuthorizeAttributeA MVC权限设计思考 (二)逻辑部分实现 在我的项目中,我还是使用的LINQ TO SQL ,因为我的项目不会涉及太多很太复杂的数据库操作业务。

    当然如果设计,我相信LINQ TO SQL的自定义扩展也能满足需求使用Repository模式是最近MVC很多项目采用的解决方案,能把原来我们杂乱的LINQ TO SQL统一封装起来让我们的架构更清晰现在来看看具体实现IRepository接口:代码 interfaceIRepositorywhereTEntity:class{IQueryableFindAll(Expression>exp);TEntityFind(Expression>exp);voidAdd(TEntityentity);voidDelete(TEntityentity);voidSave();}Repository实现:代码 publicclassRepository:IRepositorywhereTEntity:class{protectedDAL.CourseCenterDatadb;publicRepository(){db=newDAL.CourseCenterData();}///

    ///查找所有数据//////publicIQueryableFindAll(){returndb.GetTable().Where(p=>1==1);}//////查找所有数据//////条件表达式///publicIQueryableFindAll(Expression>exp){returndb.GetTable().Where(exp);}//////查找一个数据//////条件表达式///publicTEntityFind(Expression>exp){returndb.GetTable().FirstOrDefault(exp);}//////添加数据//////实体publicvoidAdd(TEntityentity){db.GetTable().InsertOnSubmit(entity);}//////删除数据//////实体publicvoidDelete(TEntityentity){db.GetTable().DeleteOnSubmit(entity);}//////批量添加数据//////实体列表publicvoidAddAll(IEnumerableentity){db.GetTable().InsertAllOnSubmit(entity);}//////批量删除数据//////实体列表publicvoidDeleteAll(IEnumerableentity){db.GetTable().DeleteAllOnSubmit(entity);}//////对数据做插入,更新,删除操作///publicvoidSave(){db.SubmitChanges();}网上已经有很多Repository的例子,但是请注意Find中的条件必须是Expression Tree的扩展,否则在数据查询的SQL语句中,你会发现捕获到的将是select一个表之后再来做数据的查询,这在我们海量数据查询时不允许的。

    现在我们已经有了自己的Repository,下面来建立一个数据库视图把我们上一讲需要的数据拿出来新建视图ViewRoleGroup,选定一下表和字段我们通过VS2008新建一个LINQ TO SQL类,拖入这个视图然后我们需要思考,这个权限分组视图是系统频繁读取的,需要为他创建一个缓存新建一个缓存类Caches:代码 ///

    ///缓存操作基类///publicclassCaches{//////建立缓存///publicstaticobjectTryAddCache(stringkey,objectvalue,CacheDependencydependencies,DateTimeabsoluteExpiration,TimeSpanslidingExpiration,CacheItemPrioritypriority,CacheItemRemovedCallbackonRemovedCallback){if(HttpRuntime.Cache[key]==null&&value!=null)returnHttpRuntime.Cache.Add(key,value,dependencies,absoluteExpiration,slidingExpiration,priority,onRemovedCallback);elsereturnnull;}//////移除缓存///publicstaticobjectTryRemoveCache(stringkey){if(HttpRuntime.Cache[key]!=null)returnHttpRuntime.Cache.Remove(key);elsereturnnull;}//////移除键中带某关键字的缓存///publicstaticvoidRemoveMultiCache(stringkeyInclude){IDictionaryEnumeratorCacheEnum=HttpRuntime.Cache.GetEnumerator();while(CacheEnum.MoveNext()){if(CacheEnum.Key.ToString().IndexOf(keyInclude.ToString())>=0)HttpRuntime.Cache.Remove(CacheEnum.Key.ToString());}}//////移除所有缓存///publicstaticvoidRemoveAllCache(){IDictionaryEnumeratorCacheEnum=HttpRuntime.Cache.GetEnumerator();while(CacheEnum.MoveNext()){HttpRuntime.Cache.Remove(CacheEnum.Key.ToString());}} }来看看我们的ViewRoleGroupRepository怎么写。

    新建类:ViewRoleGroupRepository代码 publicclassViewRoleGroupRepository:Repository{///

    ///获取权限视图缓存列表,Key:ViewGroupList//////publicListGetCacheAll(){ListviewRoleGroup;stringkey="ViewGroupList";if(HttpRuntime.Cache[key]!=null)viewRoleGroup=(List)HttpRuntime.Cache[key];else{viewRoleGroup=FindAll().ToList();Caches.TryAddCache(key,viewRoleGroup,null,Cache.NoAbsoluteExpiration,TimeSpan.FromMinutes(20),System.Web.Caching.CacheItemPriority.Normal,null);}returnviewRoleGroup;}}接下来我们将自定义自己的AuthorizeAttribute,新建类CenterAuthorizeAttribute代码 publicclassCenterAuthorizeAttribute:AuthorizeAttribute{publicoverridevoidOnAuthorization(AuthorizationContextfilterContext){stringaction=(string)filterContext.RouteData.Values["Action"];stringcontroller=(string)filterContext.RouteData.Values["Controller"];stringfullName=filterContext.HttpContext.User.Identity.Name;if(!filterContext.HttpContext.User.Identity.IsAuthenticated)filterContext.HttpContext.Response.Redirect(string.Format("~/Account/LogOn?returnUrl={0}",filterContext.HttpContext.Request.Url.PathAndQuery));else{if(newLogin().IsLock(fullName))thrownewException(string.Format("【用户:{0}】对不起,该用户已被锁定。

    ",fullName));varr=newBLL.RoleGroupRepository().GetUserRoleGroup(fullName);varq=newBLL.ViewRoleGroupRepository().GetCacheAll().FindAll(c=>c.RoleID==r.RoleID&&c.SysAppController==controller&&c.SysAppAction==action&&(c.StartTime.Equals("Anytime",StringComparison.CurrentCultureIgnoreCase)?true:(DateTime.Parse(c.StartTime)<=DateTime.Now))&&(c.EndTime.Equals("Anytime",StringComparison.CurrentCultureIgnoreCase)?true:(DateTime.Parse(c.EndTime)>=DateTime.Now)));if(q.Count==0)thrownewException(string.Format("【用户:{0}】对不起,您没有访问该页面的权限。

    ",fullName));}}}请注意varr=newBLL.RoleGroupRepository().GetUserRoleGroup(fullName);这个是我项目中通过用户名获得用户权限分组的实现,大家参考自己项目修改大功告成,那怎么用呢,很简单创建了需要的权限组,例如超级管理员 然后把对应的Controller和Action权限加入到组后,在需要的Action上进行标注,例如[CenterAuthorize]publicActionResultIndex(){ViewData["Message"]="WelcometoASP.NETMVC!";returnView();}是不是很简单就通过数据库控制到每个需要权限控制的Action咯?当然很多朋友可能还很迷糊,第一次写这类文章部分代码也没调试因为从项目剥离出来,所以大家见谅UI部分和数据库的操作这里想滤过啦这些比较简单的东西,大家应该很容易就能实现咯A MVC权限设计思考 (三)后台UI设计 一些朋友提出让我加上细节部分,也有朋友PM说很多地方看不懂不知道该怎么用,由于目前正在赶学校的课程中心,暂没有时间来详细介绍使用,这里我把后台的一些设计截图出来,大家参考设计另外在第二篇提到的CenterAuthorizeAttribute部分,已经做部分修改,原来的会出现逻辑判断的BUG。

    权限大类管理 SysAppCate可以看出,权限大类其实对应的是上部的大类菜单,当添加新的大类,该菜单将加载相应的项并呈现出来权限列表管理 SysAppList这个界面对应系统所有的权限,有朋友提出如果出现相同的Action,那么将无法分类管理,其实我认为不然,相同的Action一般用到的设计无非是Post和Get的请求变更,那么如果一个页面只可以访问却无法操作,这样还有必要呈现给用户么?大家请注意观察,在这里的设置中如果为是否现实为:False,那么该权限将不会按照分组呈现在左侧的管理菜单中权限组管理RoleGroup这个表建立我们需要的系统权限组,这样更方便我们的管理按照我的项目经验,一个项目很少会出现上百个权限组的要求,在课程中心这个项目中,当然也不会涉及到那么多的权限分类这里为每一个权限多了一个“设置”的选项,下面来看看“设置”是怎么完成的.这里我做了一个Tab,按照权限的大类分别放置所属的权限并进行加载开始时间和结束时间是控制该权限在权限组中可以操作的时间,那么设置为Anytime,也就表示开始或者结束为无限期我在该项目中间会用到申报等一些流程,那么我们必须控制时间来按照需要进行申报。

    这里我们还要考虑,应该是当权限发生的更改,才提交这个数据,否则每次都提交这么多权限进行更新,效率上讲我们犯了一个低级的错误上图的逻辑最终将更新至RoleGroupAppList表中好了,现在权限整体的结构都已经设计好,接下来将看看怎么把权限应用在对应用户上,这个很简单,但是还是比较说明一下我们现在只需要把对应的权限组分配给用户,就可以获得该组分配的权限大家还可以看到我最上面的几个截图中有“帮助” ,这并不是一个固定的页面,我通过<%= Html.ActionLink("帮助", "Help", new { c = this.ViewContext.RouteData.Values["controller"], a = this.ViewContext.RouteData.Values["action"] ,controller="System" })%>这样的形式来创建连接,那么可以把它放在MasterPage里,不同的页面中连接其实会变成对应的controller和action,这样建立对应的表,可以很简单的对应这个View视图来加载帮助信息最近手头的工作实在太忙,由于时间比较紧,就不一一介绍了。

    其实MVC给我们更大的灵活度和对视图的控制我从MVC1 RC版就开始使用MVC做一些小项目,很早抛弃了我认为相对臃肿的WEBFORM,有朋友也曾质疑MVC的编程模式接近ASP,其实这无疑是对于MVC整个框架及应用不了解所造成的Asp.Net MVC2中扩展ModelMetadata的DescriptionAttribute 在MVC2中默认并没有实现DescriptionAttribute(虽然可以找到这个属性,通过阅读MVC源码,发现并没有实现方法),这很不方便,特别是我们使用EditorForModel的时候,我们需要对字段进行简要的介绍,下面来扩展这个属性新建类 DescriptionMetadataProvider然后重写DataAnnotationsModelMetadataProvider的CreateMetadata方法:publicclassDescriptionMetadataProvider:DataAnnotationsModelMetadataProvider{protectedoverrideModelMetadataCreateMetadata(IEnumerableattributes,TypecontainerType,FuncmodelAccessor,TypemodelType,stringpropertyName){ListattributeList=newList(attributes);DataAnnotationsModelMetadataresult=(DataAnnotationsModelMetadata)base.CreateMetadata(attributes,containerType,modelAccessor,modelType,propertyName);DescriptionAttributedescriptionAttribute=attributeList.OfType().FirstOrDefault();if(descriptionAttribute!=null){result.Description=descriptionAttribute.Description;}returnresult;}}接下来在Global.asax的Application_Start()中注册DescriptionMetadataProviderModelMetadataProviders.Current=newDescriptionMetadataProvider();接着你就可以使用如下方进行标注:[DisplayName("办公地址")][Required][Description("请填写真实的办公地址")]publicstringOfficeAddress{get;set;}来看看实际效果:MVC2给我们带来了很好的模板机制,给大家提供一个学习地址:而上面扩展这个方法,我们可以这样来设计模板。

    在Shared目录中新建EditorTemplates文件夹,添加MVC用户控件Object.ascx,请注意当我们使用<%= Html.EditorForModel() %>方法的时候,MVC会自动寻找Object.ascx,其顺序是Views=>ControllerName=>EditorTemplates=>Object.ascx当在对应Controller的View中找不到Object.ascx,那么会继续在Shared=>EditorTemplates文件夹找(还会找对应模板aspx)来看看一个简单的Object.ascx代码 <%@ControlLanguage="C#"Inherits="System.Web.Mvc.ViewUserControl"%><%if(ViewData.TemplateInfo.TemplateDepth>1){%><%=ViewData.ModelMetadata.SimpleDisplayText%><%}else{%>名称内容信息<%inti=0;foreach(varpropinViewData.ModelMetadata.Properties.Where(pm=>pm.ShowForEdit&&!ViewData.TemplateInfo.Visited(pm))){%><%if(prop.HideSurroundingHtml){%><%=Html.Editor(prop.PropertyName)%><%}else{%>><%if(!String.IsNullOrEmpty(prop.DisplayName)){%><%=prop.DisplayName%><%}%><%=Html.Editor(prop.PropertyName)%><%=prop.IsRequired?"*":null%><%=Html.ValidationMessage(prop.PropertyName)%><%=prop.Description%><%}%><%i++;}%><%}%>有了这个模板的定义,大家可以尝试使用<%= Html.EditorForModel() %>方法,看看效果有什么不同呢? 。

    点击阅读更多内容