好得很程序员自学网

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

MVC系列_权限管理总结(附MVC权限管理系统源码)

MVC系列_权限管理总结(附MVC权限管理系统源码)

在上一节中我们总结了关于权限控制的方式,我们这一节讲解关于权限控制中角色权限的授予处理等等并做本系列的总结.

首先,我们来谈谈权限控制中角色权限的控制,上一节只是针对权限拦截中比较粗的控制,如果我们需要对每一个动作做细致的权限认证,我们仍然进一步设计权限处理.

比如:我们分配给一个角色只有浏览日志的权限,不允许他进行其他动作,针对这种细的粒度,我们就必须专门进行处理,就是关于角色动作验证的处理

当然,我们的数据库设计就必须就需要进一步改进.

这是我们的EF关系图:

主要看看关于tbModule,tbPermission部分,都是采用树形设计,为什么这样设计呢?

首先,我们必须要承认,Module也就是模块,应该是多层次的,就像我们的菜单,是很多级的一样,这种多层次需要我们设计成树形,授予权限,我们就可以很轻松的设计成树形进行处理.

其次,关于tbPermssion,这个是权限,但是这个权限应该也是树形结构,比如我们设计一个菜单模块权限细分分为多种,增删改查,访问,等等,一个访问可能对应多种ajax请求,比如一个ajax读取Grid信息,一个ajax读取Tree信息,如果我们要做细致处理就要对应做多级处理,虽然,小型项目用不到,但是,我们的数据库设计拥有很大的灵活性.

权限可以细分,我们授权的时候,也不会不方便,只需要前端处理的合理,做好关联的处理,

但是,如果一个Update可能有多个子集Action方法,比如Get方法,例如:部门信息管理,我们一个更新动作,是先Get部门信息,然后在进行修改以后Update,所以,Get动作就是Update的子集操作,如果我们这个都控制,小型项目会变得太过复杂,怎么处理这种东西呢?

这时候我们之前的Attribute设计的多种权限处理就派上用场了.

首先,对于多种ajax动作,如果不是对数据库很较大影响的,对于小型项目,我们根本不用管它,直接标记为只要登陆即可访问,比如Get,GetGridTree之类的,读取信息这类的子集动作,直接给他个默认的LoginAllowView标记(登录即可访问),对于更高粒度,我们当然要进行细致的控制.

这样,我们的小型项目的Action控制基本上就两级,也就是控制在增删改查这个级别,

如果我们需要更高粒度的控制,只需要去掉LoginAllowView标记,在程序中对应添加Permission,并继续给我们的Action添加子集操作,比如Add关联子集的那些动作,全部录入到数据库中,虽然过程会长一点,但是我们可以通过UI设计的更友好来处理它.

把每一个具体操作关联的Action都录入处理,我们就可以了,当然我们的数据库设计就必须合理,必须有ParentID字段,用来控制树形结构.

针对权限的处理我们就到一段落,接下来,上演关于LigerUI项目中学到的一个东西,公共查询组件的设计:

曾几何时我们拼接查询语句来设计各种搜索的高级功能,搜索,成了一块心病,这是一年前,刚开始学的时候做一个搜索的设计,拼接查询语句,

每一次的模块都是靠后台手工编码sql来设计逻辑

看到这里是不是已经吐了?????

我看了以前的东西都已经受不了了.....

也接触到了LigerUI关于Filter过滤器组件以及后台的设计,也发现一位牛人海南胡勇的组合搜索设计,

这给了我一个相当大的思路,就是设计一个通用组合搜索组件,提高复用率,设计一种解析翻译规则,我们只需要按照规则提供数据,把sql解析的工作交给组件.

这种设计是相当的震撼,而且web的开发方式可以把where放在客户端,可以在不改变后台代码的前提下,更大程度上去设计查询.

LigerUI的作者那个权限管理,我们了好久,Filter也看了好久,这里谈谈心得:

主要的模块就是这几块:

FilterGroup:查询条件组合数组

FilterParam:查询参数集合

FilterRule:规则数组

FilterTranslator:翻译机(专门负责把数据以及条件翻译成sql语句)

重要:当然为了安全性考虑我们必须对翻译机过程中的数据做校验处理

    1:      /// <summary> 
    2:        /// 用于存放过滤参数,比如一个是名称,一个是值,等价于sql中的Parameters 
    3:        /// </summary> 
    4:        public   class  FilterParam
    5:       {
    6:            public  FilterParam( string  name,  object   value )
    7:           {
    8:                this .Name = name;
    9:                this .Value =  value ;
   10:           }
   11:            public   string  Name { get; set; }
   12:            public   object  Value { get; set; }
   13:            /// <summary> 
   14:            /// 转化为ObjectParameter可变参数 
   15:            /// </summary> 
   16:            /// <returns></returns> 
   17:            public  ObjectParameter ToObjParam()
   18:           {
   19:               ObjectParameter param =  new  ObjectParameter( this .Name, this .Value);
   20:                return  param;
   21:           }
   22:            /// <summary> 
   23:            /// 为查询语句添加参数 
   24:            /// </summary> 
   25:            /// <param name="commandText">查询命令</param> 
   26:            /// <returns></returns> 
   27:            public   static   string  AddParameters( string  commandText,IEnumerable<FilterParam> listfilter)
   28:           {
   29:                foreach  (FilterParam param  in  listfilter)
   30:               {
   31:                    if  (param.Value.IsValidInput())
   32:                   {
   33:                     commandText=commandText.Replace( "@" +param.Name, "'" + param.Value.ToString()+ "'" );
   34:                   }
   35:    
   36:               }
   37:                return  commandText;
   38:           }
   39:            /// <summary> 
   40:            /// 转化为ObjectParameter可变参数 
   41:            /// </summary> 
   42:            /// <param name="listfilter"></param> 
   43:            /// <returns></returns> 
   44:            public   static  ObjectParameter[] ConvertToListObjParam(IEnumerable<FilterParam> listfilter)
   45:           {
   46:               List<ObjectParameter> list =  new  List<ObjectParameter>();
   47:                foreach  (FilterParam param  in  listfilter)
   48:               {
   49:                   list.Add(param.ToObjParam());
   50:               }
   51:                return  list.ToArray();
   52:           }

    1:     /*  作者:       tianzh 
    2:     *  创建时间:   2012/7/22 22:05:45 
    3:     * 
    4:     */ 
    5:     /*  作者:       tianzh 
    6:     *  创建时间:   2012/7/22 15:34:19 
    7:     * 
    8:     */ 
    9:    namespace  TZHSWEET.Common
   10:   {
   11:        public   class  FilterRule
   12:       {
   13:            /// <summary> 
   14:            /// 过滤规则 
   15:            /// </summary> 
   16:            public  FilterRule()
   17:           {
   18:           }
   19:            /// <summary> 
   20:            /// 过滤规则 
   21:            /// </summary> 
   22:            /// <param name="field">参数</param> 
   23:            /// <param name="value">值</param> 
   24:            public  FilterRule( string  field,  object   value )
   25:               :  this (field,  value ,  "equal" )
   26:           {
   27:           }
   28:            /// <summary> 
   29:            /// 实例化 
   30:            /// </summary> 
   31:            /// <param name="field">参数</param> 
   32:            /// <param name="value">值</param> 
   33:            /// <param name="op">操作</param> 
   34:            public  FilterRule( string  field,  object   value ,  string  op)
   35:           {
   36:                this .field = field;
   37:                this . value  =  value ;
   38:                this .op = op;
   39:           }
   40:            /// <summary> 
   41:            /// 字段 
   42:            /// </summary> 
   43:            public   string  field { get; set; }
   44:            /// <summary> 
   45:            /// 值 
   46:            /// </summary> 
   47:            public   object   value  { get; set; }
   48:            /// <summary> 
   49:            /// 操作 
   50:            /// </summary> 
   51:            public   string  op { get; set; }
   52:            /// <summary> 
   53:            /// 类型 
   54:            /// </summary> 
   55:            public   string  type { get; set; }
   56:       }
   57:   }

剩下个工作就是交给翻译机进行翻译:(对作者的版本做了修改)

    1:     /*  作者:       tianzh 
    2:     *  创建时间:   2012/7/22 22:05:49 
    3:     * 
    4:     */ 
    5:    using  System;
    6:    using  System.Collections;
    7:    using  System.Collections.Generic;
    8:    using  System.Text;
    9:    using  System.Linq;
   10:    using  System.Data.Objects;
   11:    namespace  TZHSWEET.Common
   12:   {
   13:    
   14:    
   15:        /// <summary> 
   16:        /// 将检索规则 翻译成 where sql 语句,并生成相应的参数列表 
   17:        /// 如果遇到{CurrentUserID}这种,翻译成对应的参数 
   18:        /// </summary> 
   19:        public   class  FilterTranslator
   20:       {
   21:            //几个前缀/后缀 
   22:            /// <summary> 
   23:            /// 左中括号[(用于表示数据库实体前的标识) 
   24:            /// </summary> 
   25:            protected   char  leftToken =  '[' ;
   26:            /// <summary> 
   27:            /// 用于可变参替换的标志 
   28:            /// </summary> 
   29:            protected   char  paramPrefixToken =  '@' ;
   30:            /// <summary> 
   31:            /// 右中括号(用于表示数据库实体前的标识) 
   32:            /// </summary> 
   33:            protected   char  rightToken =  ']' ;
   34:            /// <summary> 
   35:            /// 组条件括号 
   36:            /// </summary> 
   37:            protected   char  groupLeftToken =  '(' ;
   38:            /// <summary> 
   39:            /// 右条件括号 
   40:            /// </summary> 
   41:            protected   char  groupRightToken =  ')' ;
   42:            /// <summary> 
   43:            /// 模糊查询符号 
   44:            /// </summary> 
   45:            protected   char  likeToken =  '%' ;
   46:            /// <summary> 
   47:            /// 参数计数器 
   48:            /// </summary> 
   49:            private   int  paramCounter = 0;
   50:    
   51:            //几个主要的属性 
   52:            public  FilterGroup Group { get; set; }
   53:            /// <summary> 
   54:            /// 最终的Where语句(包括可变参占位符) 
   55:            /// </summary> 
   56:            public   string  CommandText { get;  private  set; }
   57:            /// <summary> 
   58:            /// 查询语句可变参数数组 
   59:            /// </summary> 
   60:            public  IList<FilterParam> Parms { get;  private  set; }
   61:            /// <summary> 
   62:            /// 是否为Entity To Sql 生成where翻译语句(Entity To Sql就需要在实体前面加it,例如it.ID=@ID and it.Name-@Name) 
   63:            /// 否则为普通的SQL语句可变参拼接 
   64:            /// </summary> 
   65:            public   bool  IsEntityToSql { get; set; }
   66:            public  FilterTranslator()
   67:               :  this ( null )
   68:           {
   69:               IsEntityToSql =  false ;
   70:           }
   71:            /// <summary> 
   72:            /// 构造函数 
   73:            /// </summary> 
   74:            /// <param name="group"></param> 
   75:            public  FilterTranslator(FilterGroup group)
   76:           {
   77:                this .Group = group;
   78:                this .Parms =  new  List<FilterParam>();
   79:           }
   80:       
   81:            /// <summary> 
   82:            /// 翻译语句成sql的where查询条件 
   83:            /// </summary> 
   84:            public   void  Translate()
   85:           {
   86:                this .CommandText = TranslateGroup( this .Group);
   87:           }
   88:            /// <summary> 
   89:            /// 对多组规则进行翻译解析 
   90:            /// </summary> 
   91:            /// <param name="group">规则数组</param> 
   92:            /// <returns></returns> 
   93:            public   string  TranslateGroup(FilterGroup group)
   94:           {
   95:               StringBuilder bulider =  new  StringBuilder();
   96:                if  (group ==  null )  return   " 1=1 " ;
   97:               var appended =  false ;
   98:               bulider.Append(groupLeftToken);
   99:                if  (group.rules !=  null )
  100:               {
  101:                    foreach  (var rule  in  group.rules)
  102:                   {
  103:                        if  (appended)
  104:                           bulider.Append(GetOperatorQueryText(group.op));
  105:                       bulider.Append(TranslateRule(rule));
  106:                       appended =  true ;
  107:                   }
  108:               }
  109:                if  (group.groups !=  null )
  110:               {
  111:                    foreach  (var subgroup  in  group.groups)
  112:                   {
  113:                        if  (appended)
  114:                           bulider.Append(GetOperatorQueryText(group.op));
  115:                       bulider.Append(TranslateGroup(subgroup));
  116:                       appended =  true ;
  117:                   }
  118:               }
  119:               bulider.Append(groupRightToken);
  120:                if  (appended ==  false )  return   " 1=1 " ;
  121:                return  bulider.ToString();
  122:           }
  123:    
  124:            /// <summary> 
  125:            /// 注册用户匹配管理,当不方便修改ligerRM.dll时,可以通过这种方式,在外部注册 
  126:            ///  currentParmMatch.Add("{CurrentUserID}",()=>UserID); 
  127:            /// currentParmMatch.Add("{CurrentRoleID}",()=>UserRoles.Split(',')[0].ObjToInt()); 
  128:            /// </summary> 
  129:            /// <param name="match"></param> 
  130:            public   static   void  RegCurrentParmMatch( string  key,Func< int > fn)
  131:           {
  132:                if  (!currentParmMatch.ContainsKey(key))
  133:                   currentParmMatch.Add(key, fn);
  134:           }
  135:    
  136:            /// <summary> 
  137:            /// 匹配当前用户信息,都是int类型 
  138:            /// 对于CurrentRoleID,只返回第一个角色 
  139:            /// 注意这里是用来定义隐藏规则,比如,用户只能自己访问等等, 
  140:            /// </summary> 
  141:            private   static  Dictionary< string , Func< int >> currentParmMatch =  new  Dictionary< string , Func< int >>()
  142:           {};
  143:            /// <summary> 
  144:            /// 翻译规则 
  145:            /// </summary> 
  146:            /// <param name="rule">规则</param> 
  147:            /// <returns></returns> 
  148:            public   string  TranslateRule(FilterRule rule)
  149:           {
  150:             
  151:               StringBuilder bulider =  new  StringBuilder();
  152:                if  (rule ==  null )  return   " 1=1 " ;
  153:    
  154:                //如果字段名采用了 用户信息参数 
  155:                if  (currentParmMatch.ContainsKey(rule.field))
  156:               {
  157:                   var field = currentParmMatch[rule.field]();
  158:                   bulider.Append(paramPrefixToken + CreateFilterParam(field,  "int" ));
  159:               }
  160:                else   //这里实现了数据库实体条件的拼接,[ID]=xxx的形式 
  161:               {
  162:                  
  163:                   //如果是EF To Sql 
  164:                    if  (IsEntityToSql)
  165:                   {
  166:                       bulider.Append( " it."  + rule.field+ " " );
  167:                   }
  168:                    else 
  169:                   {
  170:                       bulider.Append(leftToken + rule.field + rightToken);
  171:                   }
  172:               }
  173:                //操作符 
  174:               bulider.Append(GetOperatorQueryText(rule.op));
  175:    
  176:               var op = rule.op.ToLower();
  177:                if  (op ==  "like"  || op ==  "endwith" )
  178:               {
  179:                   var  value  = rule. value .ToString();
  180:                    if  (! value .StartsWith( this .likeToken.ToString()))
  181:                   {
  182:                       rule. value  =  this .likeToken +  value ;
  183:                   }
  184:               }
  185:                if  (op ==  "like"  || op ==  "startwith" )
  186:               {
  187:                   var  value  = rule. value .ToString();
  188:                    if  (! value .EndsWith( this .likeToken.ToString()))
  189:                   {
  190:                       rule. value  =  value  +  this .likeToken;
  191:                   }
  192:               }
  193:                if  (op ==  "in"  || op ==  "notin" )
  194:               {
  195:                   var values = rule. value .ToString().Split( ',' );
  196:                   var appended =  false ;
  197:                   bulider.Append( "(" );
  198:                    foreach  (var  value   in  values)
  199:                   {
  200:                        if  (appended) bulider.Append( "," );
  201:                        //如果值使用了 用户信息参数 比如: in ({CurrentRoleID},4) 
  202:                        if  (currentParmMatch.ContainsKey( value ))
  203:                       {
  204:                           var val = currentParmMatch[ value ]();
  205:                           bulider.Append(paramPrefixToken + CreateFilterParam(val,  "int" ));
  206:                       }
  207:                        else 
  208:                       {
  209:                           bulider.Append(paramPrefixToken + CreateFilterParam( value , rule.type)); 
  210:                       }
  211:                       appended =  true ;
  212:                   }
  213:                   bulider.Append( ")" );
  214:               } 
  215:                //is null 和 is not null 不需要值 
  216:                else   if  (op !=  "isnull"  && op !=  "isnotnull" )
  217:               {
  218:                    //如果值使用了 用户信息参数 比如 [EmptID] = {CurrentEmptID} 
  219:                    if  (rule. value  !=  null  && currentParmMatch.ContainsKey(rule. value .ObjToStr()))
  220:                   {
  221:                       var  value  = currentParmMatch[rule. value .ObjToStr()]();
  222:                       bulider.Append(paramPrefixToken + CreateFilterParam( value ,  "int" ));
  223:                   }
  224:                    else 
  225:                   {
  226:                       bulider.Append(paramPrefixToken + CreateFilterParam(rule. value , rule.type));
  227:    
  228:                   }
  229:               } 
  230:                return  bulider.ToString();
  231:           }
  232:            /// <summary> 
  233:            /// 创建过滤规则参数数组 
  234:            /// </summary> 
  235:            /// <param name="value"></param> 
  236:            /// <param name="type"></param> 
  237:            /// <returns></returns> 
  238:            private   string  CreateFilterParam( object   value , string  type)
  239:           {
  240:              
  241:                string  paramName =  "p"  + ++paramCounter;
  242:                object  val =  value ;
  243:              
  244:            
  245:                ////原版在这里要验证类型 
  246:                //if (type.Equals("int", StringComparison.OrdinalIgnoreCase) || type.Equals("digits", StringComparison.OrdinalIgnoreCase)) 
  247:                //    val = val.ObjToInt (); 
  248:                //if (type.Equals("float", StringComparison.OrdinalIgnoreCase) || type.Equals("number", StringComparison.OrdinalIgnoreCase)) 
  249:                //    val = type.ObjToDecimal(); 
  250:    
  251:               FilterParam param =  new  FilterParam(paramName, val);
  252:                this .Parms.Add(param);
  253:                return  paramName;
  254:           }
  255:          
  256:            /// <summary> 
  257:            /// 获取解析的参数 
  258:            /// </summary> 
  259:            /// <returns></returns> 
  260:            public   override   string  ToString()
  261:           {
  262:               StringBuilder bulider =  new  StringBuilder();
  263:               bulider.Append( "CommandText:" );
  264:               bulider.Append( this .CommandText);
  265:               bulider.AppendLine();
  266:               bulider.AppendLine( "Parms:" );
  267:                foreach  (var parm  in   this .Parms)
  268:               {
  269:                   bulider.AppendLine( string .Format( "{0}:{1}" , parm.Name, parm.Value));
  270:               }
  271:                return  bulider.ToString();
  272:           }
  273:      
  274:            #region  公共工具方法
  275:            /// <summary> 
  276:            /// 获取操作符的SQL Text 
  277:            /// </summary> 
  278:            /// <param name="op"></param> 
  279:            /// <returns></returns>  
  280:            public   static   string  GetOperatorQueryText( string  op)
  281:           {
  282:                switch  (op.ToLower())
  283:               {
  284:                    case   "add" :
  285:                        return   " + " ;
  286:                    case   "bitwiseand" :
  287:                        return   " & " ;
  288:                    case   "bitwisenot" :
  289:                        return   " ~ " ;
  290:                    case   "bitwiseor" :
  291:                        return   " | " ;
  292:                    case   "bitwisexor" :
  293:                        return   " ^ " ;
  294:                    case   "divide" :
  295:                        return   " / " ;
  296:                    case   "equal" :
  297:                        return   " = " ;
  298:                    case   "greater" :
  299:                        return   " > " ;
  300:                    case   "greaterorequal" :
  301:                        return   " >= " ;
  302:                    case   "isnull" :
  303:                        return   " is null " ;
  304:                    case   "isnotnull" :
  305:                        return   " is not null " ;
  306:                    case   "less" :
  307:                        return   " < " ;
  308:                    case   "lessorequal" :
  309:                        return   " <= " ;
  310:                    case   "like" :
  311:                        return   " like " ;
  312:                    case   "startwith" :
  313:                        return   " like " ;
  314:                    case   "endwith" :
  315:                        return   " like " ;
  316:                    case   "modulo" :
  317:                        return   " % " ;
  318:                    case   "multiply" :
  319:                        return   " * " ;
  320:                    case   "notequal" :
  321:                        return   " <> " ;
  322:                    case   "subtract" :
  323:                        return   " - " ;
  324:                    case   "and" :
  325:                        return   " and " ;
  326:                    case   "or" :
  327:                        return   " or " ;
  328:                    case   "in" :
  329:                        return   " in " ;
  330:                    case   "notin" :
  331:                        return   " not in " ;
  332:                    default :
  333:                        return   " = " ;
  334:               }
  335:           }
  336:            #endregion 
  337:    
  338:       }
  339:   }

可能大家说,这玩意怎么用呀?LigerUI做了一个专门针对组合查询的组件,也可以自己去写,有了开源的代码,相信我们自己也可以写出自己的组件.

我们前台的搜索设计就更容易了:

看看我的日志搜索模块怎么设置的搜索

    1:          //搜索表单应用ligerui样式 
    2:         $( "#formsearch" ).ligerForm({
    3:             fields: [
    4:              {display:  "用户名" , name:  "UserName" , newline:  true , labelWidth: 100,  220, space: 30, type:  "text" ,
    5:                   attr: { op:  "equal"  }, cssClass:  "field" }
    6:                   ,
    7:              { display:  "IP地址" , name:  "IPAddress" , newline:  false , labelWidth: 100,  220, space: 30, type:  "text" , cssClass:  "field" },
    8:              { display:  "开始时间" , name:  "CreateDate" , newline:  true , labelWidth: 100,  220, space: 30, type:  "date" , cssClass:  "field" , attr: {  "op" :  "greaterorequal" }},
    9:              { display:  "结束时间" , name:  "CreateDate" , newline: false  , labelWidth: 100,  220, space: 30, type:  "date" , cssClass:  "field" , attr: {  "op" :  "lessorequal" }}
   10:              ],
   11:             appendID:  false ,
   12:             toJSON: JSON2.stringify
   13:         });
   14:    
   15:          //增加搜索按钮,并创建事件 
   16:         LG.appendSearchButtons( "#formsearch" , grid);

也就是说,我们只需要设置规则,甚至可以自己去按照json格式传递给后台我们的规则就可以了,比如:

如果我们的Grid想设计条件,可以直接这么加

直接把这个where条件json化传递给后台就可以实现我们的按照条件查询Grid功能了.

这时候,大家想到了什么?

我们把条件where的部分更多的分担在UI层,我们的后台业务逻辑只需要解析where就可以再不改变业务逻辑的的条件下实现更复杂的业务逻辑.

其实,海南胡勇那位牛人设计的winform查询组件也是这个道理.

如果你还停留在拼接查询语句阶段,可以看看他们的设计.

最后,整个项目实际上,我非常不满意的是架构,这部分太差了,没有真正的公司工作经验,仅仅是粗浅的理解,深入学习,<<企业架构模式>>这本书买了也看不懂,缺少真正的工作经验,谈架构就是扯淡,所以,大家见谅.

如果觉得不错就推荐一下,支持一下吧!呵呵!

感谢,博客园的众多牛人提供了太多的学习资料和项目,让我们这些没有毕业的学生也有更多的学习资料.

这里也提供源码分享给更多学习的人.

注:推荐使用IE9,或者谷歌!IE8出现了BUG......呃

附带源码网盘地址: http://pan.baidu测试数据/netdisk/singlepublic?fid=511632_3611299055

设计的主要截图:

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

分类:  .net ,  ajax技术学习 ,  asp.net MVC ,  JQuery ,  权限管理设计 ,  Entity FrameWork实战

作者: Leo_wl

    

出处: http://HdhCmsTestcnblogs测试数据/Leo_wl/

    

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

版权信息

查看更多关于MVC系列_权限管理总结(附MVC权限管理系统源码)的详细内容...

  阅读:129次