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

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

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

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

这是我们的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语句)

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

:    /// <summary>
: /// 用于存放过滤参数,比如一个是名称,一个是值,等价于sql中的Parameters
: /// </summary>
: public class FilterParam
: {
: public FilterParam(string name, object value)
: {
: this.Name = name;
: this.Value = value;
: }
: public string Name { get; set; }
: public object Value { get; set; }
: /// <summary>
: /// 转化为ObjectParameter可变参数
: /// </summary>
: /// <returns></returns>
: public ObjectParameter ToObjParam()
: {
: ObjectParameter param = new ObjectParameter(this.Name,this.Value);
: return param;
: }
: /// <summary>
: /// 为查询语句添加参数
: /// </summary>
: /// <param name="commandText">查询命令</param>
: /// <returns></returns>
: public static string AddParameters(string commandText,IEnumerable<FilterParam> listfilter)
: {
: foreach (FilterParam param in listfilter)
: {
: if (param.Value.IsValidInput())
: {
: commandText=commandText.Replace("@"+param.Name,"'"+ param.Value.ToString()+"'");
: }
:
: }
: return commandText;
: }
: /// <summary>
: /// 转化为ObjectParameter可变参数
: /// </summary>
: /// <param name="listfilter"></param>
: /// <returns></returns>
: public static ObjectParameter[] ConvertToListObjParam(IEnumerable<FilterParam> listfilter)
: {
: List<ObjectParameter> list = new List<ObjectParameter>();
: foreach (FilterParam param in listfilter)
: {
: list.Add(param.ToObjParam());
: }
: return list.ToArray();
: }
: /* 作者: tianzh
2: * 创建时间: 2012/7/22 22:05:45
3: *
4: */
: /* 作者: tianzh
6: * 创建时间: 2012/7/22 15:34:19
7: *
8: */
: namespace TZHSWEET.Common
: {
: public class FilterRule
: {
: /// <summary>
: /// 过滤规则
: /// </summary>
: public FilterRule()
: {
: }
: /// <summary>
: /// 过滤规则
: /// </summary>
: /// <param name="field">参数</param>
: /// <param name="value">值</param>
: public FilterRule(string field, object value)
: : this(field, value, "equal")
: {
: }
: /// <summary>
: /// 实例化
: /// </summary>
: /// <param name="field">参数</param>
: /// <param name="value">值</param>
: /// <param name="op">操作</param>
: public FilterRule(string field, object value, string op)
: {
: this.field = field;
: this.value = value;
: this.op = op;
: }
: /// <summary>
: /// 字段
: /// </summary>
: public string field { get; set; }
: /// <summary>
: /// 值
: /// </summary>
: public object value { get; set; }
: /// <summary>
: /// 操作
: /// </summary>
: public string op { get; set; }
: /// <summary>
: /// 类型
: /// </summary>
: public string type { get; set; }
: }
: }

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

  :   /*  作者:       tianzh
2: * 创建时间: 2012/7/22 22:05:49
3: *
4: */
: using System;
: using System.Collections;
: using System.Collections.Generic;
: using System.Text;
: using System.Linq;
: using System.Data.Objects;
: namespace TZHSWEET.Common
: {
:
:
: /// <summary>
: /// 将检索规则 翻译成 where sql 语句,并生成相应的参数列表
: /// 如果遇到{CurrentUserID}这种,翻译成对应的参数
: /// </summary>
: public class FilterTranslator
: {
: //几个前缀/后缀
: /// <summary>
: /// 左中括号[(用于表示数据库实体前的标识)
: /// </summary>
: protected char leftToken = '[';
: /// <summary>
: /// 用于可变参替换的标志
: /// </summary>
: protected char paramPrefixToken = '@';
: /// <summary>
: /// 右中括号(用于表示数据库实体前的标识)
: /// </summary>
: protected char rightToken = ']';
: /// <summary>
: /// 组条件括号
: /// </summary>
: protected char groupLeftToken = '(';
: /// <summary>
: /// 右条件括号
: /// </summary>
: protected char groupRightToken = ')';
: /// <summary>
: /// 模糊查询符号
: /// </summary>
: protected char likeToken = '%';
: /// <summary>
: /// 参数计数器
: /// </summary>
: private int paramCounter = ;
:
: //几个主要的属性
: public FilterGroup Group { get; set; }
: /// <summary>
: /// 最终的Where语句(包括可变参占位符)
: /// </summary>
: public string CommandText { get; private set; }
: /// <summary>
: /// 查询语句可变参数数组
: /// </summary>
: public IList<FilterParam> Parms { get; private set; }
: /// <summary>
: /// 是否为Entity To Sql 生成where翻译语句(Entity To Sql就需要在实体前面加it,例如it.ID=@ID and it.Name-@Name)
: /// 否则为普通的SQL语句可变参拼接
: /// </summary>
: public bool IsEntityToSql { get; set; }
: public FilterTranslator()
: : this(null)
: {
: IsEntityToSql = false;
: }
: /// <summary>
: /// 构造函数
: /// </summary>
: /// <param name="group"></param>
: public FilterTranslator(FilterGroup group)
: {
: this.Group = group;
: this.Parms = new List<FilterParam>();
: }
:
: /// <summary>
: /// 翻译语句成sql的where查询条件
: /// </summary>
: public void Translate()
: {
: this.CommandText = TranslateGroup(this.Group);
: }
: /// <summary>
: /// 对多组规则进行翻译解析
: /// </summary>
: /// <param name="group">规则数组</param>
: /// <returns></returns>
: public string TranslateGroup(FilterGroup group)
: {
: StringBuilder bulider = new StringBuilder();
: if (group == null) return " 1=1 ";
: var appended = false;
: bulider.Append(groupLeftToken);
: if (group.rules != null)
: {
: foreach (var rule in group.rules)
: {
: if (appended)
: bulider.Append(GetOperatorQueryText(group.op));
: bulider.Append(TranslateRule(rule));
: appended = true;
: }
: }
: if (group.groups != null)
: {
: foreach (var subgroup in group.groups)
: {
: if (appended)
: bulider.Append(GetOperatorQueryText(group.op));
: bulider.Append(TranslateGroup(subgroup));
: appended = true;
: }
: }
: bulider.Append(groupRightToken);
: if (appended == false) return " 1=1 ";
: return bulider.ToString();
: }
:
: /// <summary>
: /// 注册用户匹配管理,当不方便修改ligerRM.dll时,可以通过这种方式,在外部注册
: /// currentParmMatch.Add("{CurrentUserID}",()=>UserID);
: /// currentParmMatch.Add("{CurrentRoleID}",()=>UserRoles.Split(',')[0].ObjToInt());
: /// </summary>
: /// <param name="match"></param>
: public static void RegCurrentParmMatch(string key,Func<int> fn)
: {
: if (!currentParmMatch.ContainsKey(key))
: currentParmMatch.Add(key, fn);
: }
:
: /// <summary>
: /// 匹配当前用户信息,都是int类型
: /// 对于CurrentRoleID,只返回第一个角色
: /// 注意这里是用来定义隐藏规则,比如,用户只能自己访问等等,
: /// </summary>
: private static Dictionary<string, Func<int>> currentParmMatch = new Dictionary<string, Func<int>>()
: {};
: /// <summary>
: /// 翻译规则
: /// </summary>
: /// <param name="rule">规则</param>
: /// <returns></returns>
: public string TranslateRule(FilterRule rule)
: {
:
: StringBuilder bulider = new StringBuilder();
: if (rule == null) return " 1=1 ";
:
: //如果字段名采用了 用户信息参数
: if (currentParmMatch.ContainsKey(rule.field))
: {
: var field = currentParmMatch[rule.field]();
: bulider.Append(paramPrefixToken + CreateFilterParam(field, "int"));
: }
: else //这里实现了数据库实体条件的拼接,[ID]=xxx的形式
: {
:
: //如果是EF To Sql
: if (IsEntityToSql)
: {
: bulider.Append(" it." + rule.field+" ");
: }
: else
: {
: bulider.Append(leftToken + rule.field + rightToken);
: }
: }
: //操作符
: bulider.Append(GetOperatorQueryText(rule.op));
:
: var op = rule.op.ToLower();
: if (op == "like" || op == "endwith")
: {
: var value = rule.value.ToString();
: if (!value.StartsWith(this.likeToken.ToString()))
: {
: rule.value = this.likeToken + value;
: }
: }
: if (op == "like" || op == "startwith")
: {
: var value = rule.value.ToString();
: if (!value.EndsWith(this.likeToken.ToString()))
: {
: rule.value = value + this.likeToken;
: }
: }
: if (op == "in" || op == "notin")
: {
: var values = rule.value.ToString().Split(',');
: var appended = false;
: bulider.Append("(");
: foreach (var value in values)
: {
: if (appended) bulider.Append(",");
: //如果值使用了 用户信息参数 比如: in ({CurrentRoleID},4)
: if (currentParmMatch.ContainsKey(value))
: {
: var val = currentParmMatch[value]();
: bulider.Append(paramPrefixToken + CreateFilterParam(val, "int"));
: }
: else
: {
: bulider.Append(paramPrefixToken + CreateFilterParam(value, rule.type));
: }
: appended = true;
: }
: bulider.Append(")");
: }
: //is null 和 is not null 不需要值
: else if (op != "isnull" && op != "isnotnull")
: {
: //如果值使用了 用户信息参数 比如 [EmptID] = {CurrentEmptID}
: if (rule.value != null && currentParmMatch.ContainsKey(rule.value.ObjToStr()))
: {
: var value = currentParmMatch[rule.value.ObjToStr()]();
: bulider.Append(paramPrefixToken + CreateFilterParam(value, "int"));
: }
: else
: {
: bulider.Append(paramPrefixToken + CreateFilterParam(rule.value, rule.type));
:
: }
: }
: return bulider.ToString();
: }
: /// <summary>
: /// 创建过滤规则参数数组
: /// </summary>
: /// <param name="value"></param>
: /// <param name="type"></param>
: /// <returns></returns>
: private string CreateFilterParam(object value,string type)
: {
:
: string paramName = "p" + ++paramCounter;
: object val = value;
:
:
: ////原版在这里要验证类型
: //if (type.Equals("int", StringComparison.OrdinalIgnoreCase) || type.Equals("digits", StringComparison.OrdinalIgnoreCase))
: // val = val.ObjToInt ();
: //if (type.Equals("float", StringComparison.OrdinalIgnoreCase) || type.Equals("number", StringComparison.OrdinalIgnoreCase))
: // val = type.ObjToDecimal();
:
: FilterParam param = new FilterParam(paramName, val);
: this.Parms.Add(param);
: return paramName;
: }
:
: /// <summary>
: /// 获取解析的参数
: /// </summary>
: /// <returns></returns>
: public override string ToString()
: {
: StringBuilder bulider = new StringBuilder();
: bulider.Append("CommandText:");
: bulider.Append(this.CommandText);
: bulider.AppendLine();
: bulider.AppendLine("Parms:");
: foreach (var parm in this.Parms)
: {
: bulider.AppendLine(string.Format("{0}:{1}", parm.Name, parm.Value));
: }
: return bulider.ToString();
: }
:
: #region 公共工具方法
: /// <summary>
: /// 获取操作符的SQL Text
: /// </summary>
: /// <param name="op"></param>
: /// <returns></returns>
: public static string GetOperatorQueryText(string op)
: {
: switch (op.ToLower())
: {
: case "add":
: return " + ";
: case "bitwiseand":
: return " & ";
: case "bitwisenot":
: return " ~ ";
: case "bitwiseor":
: return " | ";
: case "bitwisexor":
: return " ^ ";
: case "divide":
: return " / ";
: case "equal":
: return " = ";
: case "greater":
: return " > ";
: case "greaterorequal":
: return " >= ";
: case "isnull":
: return " is null ";
: case "isnotnull":
: return " is not null ";
: case "less":
: return " < ";
: case "lessorequal":
: return " <= ";
: case "like":
: return " like ";
: case "startwith":
: return " like ";
: case "endwith":
: return " like ";
: case "modulo":
: return " % ";
: case "multiply":
: return " * ";
: case "notequal":
: return " <> ";
: case "subtract":
: return " - ";
: case "and":
: return " and ";
: case "or":
: return " or ";
: case "in":
: return " in ";
: case "notin":
: return " not in ";
: default:
: return " = ";
: }
: }
: #endregion
:
: }
: }

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

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

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

  :        //搜索表单应用ligerui样式
: $("#formsearch").ligerForm({
: fields: [
: {display: "用户名", name: "UserName", newline: true, labelWidth: , width: , space: , type: "text",
: attr: { op: "equal" }, cssClass: "field"}
: ,
: { display: "IP地址", name: "IPAddress", newline: false, labelWidth: , width: , space: , type: "text", cssClass: "field"},
: { display: "开始时间", name: "CreateDate", newline: true, labelWidth: , width: , space: , type: "date", cssClass: "field", attr: { "op": "greaterorequal"}},
: { display: "结束时间", name: "CreateDate", newline:false , labelWidth: , width: , space: , type: "date", cssClass: "field", attr: { "op": "lessorequal"}}
: ],
: appendID: false,
: toJSON: JSON2.stringify
: });
:
: //增加搜索按钮,并创建事件
: LG.appendSearchButtons("#formsearch", grid);

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

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

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

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

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

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

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

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

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

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

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

注:推荐使用IE9,或者谷歌!IE8出现了BUG......呃!另外忘了说了,这个是vs2010开发,数据库是sql2005。。请确保安装mvc3,vs2010,sql2005。。。。。额。。。

 

目前1.1下载版本

附带源码网盘地址:http://pan.baidu.com/netdisk/singlepublic?fid=511632_114595096

设计的主要截图:

 

原文自 http://www.cnblogs.com/mysweet/archive/2012/08/07/2626954.html

一步一步Asp.Net MVC系列_权限管理总结(附MVC权限管理系统源码)的更多相关文章

  1. java io系列12之 BufferedInputStream(缓冲输入流)的认知、源码和示例

    本章内容包括3个部分:BufferedInputStream介绍,BufferedInputStream源码,以及BufferedInputStream使用示例. 转载请注明出处:http://www ...

  2. ABP入门系列(12)——如何升级Abp并调试源码

    ABP入门系列目录--学习Abp框架之实操演练 源码路径:Github-LearningMpaAbp 1. 升级Abp 本系列教程是基于Abp V1.0版本,现在Abp版本已经升级至V1.4.2(截至 ...

  3. java io系列15之 DataOutputStream(数据输出流)的认知、源码和示例

    本章介绍DataOutputStream.我们先对DataOutputStream有个大致认识,然后再深入学习它的源码,最后通过示例加深对它的了解. 转载请注明出处:http://www.cnblog ...

  4. Spring Boot 2.0系列文章(五):Spring Boot 2.0 项目源码结构预览

    关注我 转载请务必注明原创地址为:http://www.54tianzhisheng.cn/2018/04/15/springboot2_code/ 项目结构 结构分析: Spring-boot-pr ...

  5. asp微信支付代码证书文件post_url.aspx和post_url.aspx.cs源码下载

    很多朋友在网上找的asp支付代码中都没有这两个证书文件,只能是用别人的,但是如果别人把他的网站这个文件删了,你的支付也就不能用了,今天我就把大家需要的这两个asp微信支付代码证书文件post_url. ...

  6. java io系列13之 BufferedOutputStream(缓冲输出流)的认知、源码和示例

    本章内容包括3个部分:BufferedOutputStream介绍,BufferedOutputStream源码,以及BufferedOutputStream使用示例. 转载请注明出处:http:// ...

  7. 《ASP.NET SignalR系列》第五课 在MVC中使用SignalR

    接着上一篇:<ASP.NET SignalR系列>第四课 SignalR自托管(不用IIS) 一.概述 本教程主要阐释了如何在MVC下使用ASP.NET SignalR. 添加Signal ...

  8. 【.NET特供-第三季】ASP.NET MVC系列:传统WebForm站点和MVC站点执行机制对照

    本文以图形化的方式,从'执行机制'方面对照传统WebForm站点和MVC站点. 请參看下面图形: watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvemhhb2 ...

  9. Cordova+Asp.net Mvc+GIS跨平台移动应用开发实战1-系统初步搭建(附演示,apk,全部源码)

    1.前言 身处在移动互联网的今天,移动应用开发炙手可热,身为程序猿的我们怎么能错过开发一款我们自己的APP.本人算是一个基于.net的GIS开发入门者(马上就大四啦), 暑假在学校参加GIS比赛有大把 ...

随机推荐

  1. js 中 setTimeout()的用法

    setTimeout()在js类中的使用方法   setTimeout (表达式,延时时间)setTimeout(表达式,交互时间)延时时间/交互时间是以豪秒为单位的(1000ms=1s) setTi ...

  2. php中禁止非法调用和硬路径引入文件的方法

    php中禁止非法调用和硬路径引入文件的方法 在php中有一些公共的文件为了方便,我们会做一个公共文件,让不用的文件共同调用.为了禁止公共文件被非常单独调用,可以在文件上做一个常量,禁止非常调用:在公共 ...

  3. substr(dirname(__FILE__))

    这是discuz中定义论坛安装根目录的一个常量.现在我们就来分析一下这个很简单但是非常实用的常量.     define('DISCUZ_ROOT', substr(dirname(__FILE__) ...

  4. python之字符串格式化(format)

    用法: 它通过{}和:来代替传统%方式 1.使用位置参数 要点:从以下例子可以看出位置参数不受顺序约束,且可以为{},只要format里有相对应的参数值即可,参数索引从0开,传入位置参数列表可用*列表 ...

  5. OC中NSArray的使用

    不可变数组类容器类,管理一组对象类型的数据.   元素是有序的,索引值从0开始  数组中存储的元素必须是对象,类型任意.   创建数组对象,使⽤用实例初始化或便利构造器.获取元素个数.根据索引值获取对 ...

  6. FLAG_ACTIVITY_NEW_TASK和SingleInstance的设计思路(多task的应用)

    这部分的想法都是基于以下两点: 1.Activity可能被复用,可能是复用Activity的功能,还可能是复用Activity的状态: 2.Task的作用:target,同一个task中的Activi ...

  7. oracle正则表达式regexp_like的用法详解

    oracle正则表达式regexp_like的用法详解 /*ORACLE中的支持正则表达式的函数主要有下面四个:1,REGEXP_LIKE :与LIKE的功能相似2,REGEXP_INSTR :与IN ...

  8. Windows Azure 网站:应用程序字符串和连接字符串的工作原理

    编辑人员注释:本文章由 Windows Azure 网站团队的首席项目经理 Stefan Schackow 撰写. Windows Azure 网站上有一个方便的功能,即开发人员可将 Azure 中的 ...

  9. 发布MFC ActiveX控件并实现自动更新

    一.        引言 上一篇我们讲了如何使用 VC 2005来开发 MFC ActiveX控件,我们开发 ActiveX控件最终目的是将 ActiveX控件发布出来并嵌入在 Web网页中,随着控件 ...

  10. Oracle 11g新特性虚拟列分区

    如今有个需求:一个单据表要依照月份来分区.假设是在Oracle 10g上,仅仅能再加一个字段. 在Oracle 11g以后就不一样了.能够用虚拟列处理. SQL> select * from v ...