Tonny's Life : 首页(28) 程序设计(5) 生活点滴(15) 电商传奇(1) 体育健身(2) 

    关注:因为本blog不能匿名回复,所以给大家申请了一个公共帐号:frd(密码123456)可以回复了,当然不要随便改密码,谢谢了

[程序设计]权限设计问题,即C#实现上的设想[供讨论] 【zz】
软件技术

tonny 发表于 2005/3/26 10:51:11

现在的程序,很多涉及到权限这个管理问题。一稍大和政务或商务软件都会涉及许多的权限,如管理、发表、浏览、收发公告等权限。对这个问题,在数据库上怎样实现才好呢?500)this.width=500'>我下载了一些软件,它们在数据库上实现是用字段,即每一权限设置为1个Boolean或byte的字段。我分析了这种设计,优点是读取方便,如要知道某一权限,只要得到某个字段就可以了,不需要很多的程序来判断。500)this.width=500'>但这种设计的缺点是:1、在维护中,需要增加或修改权限,就需要修改数据库;2、如果贪其方便,很可能会在程序很多地方用到判断一个权限的语句。如果权限改变或取消后,会在很多的地方修改代码;3、字段很长,如果有几十种权即的话,会较麻烦。500)this.width=500'>听取朋友建议后,考虑用一个字段,用01来表示是否拥有该权限,权限按位设置:100100101110110,每位代表一种权限。考虑以后的升级等需要,应设置一些预留位。比如一家公司需要40种权限,在设计时就用100位的字串来表示,如:·管理员:    需要12种,设置前20位·发表文章:需要20种,设置30位·工作权限:需要20种,设置30位......没有用到的权限位全部设为0,500)this.width=500'>这样我们可以来解码了。 不细说这位数怎样记录,这在用户管理中还是比较方便的。我主要想说一下如何读取:500)this.width=500'>这10011011101......太难读懂了,好象是个机械,一点没人味,我就想给它增加些人味好在这C#比以前用ASP强太多了,就想到了用枚举、结构,用类的封装。该类的封装核心应该是读取和解码首先用个enum来列举一下这个权限,也就是给每个位(权限)取个好听的名字:enum PersonPurview {IsAdmin, ManageMember, ManageArticle, ......,                                 ManagePreSet1, ManagePreSet2, ......,                                 ArticleSend, ArticleView, .....,                                 ArticlePreSet1, ArticlePreSet2, ......};当然你可以用结构,但我发觉用枚举似乎更方便。比如我们从数据库中已经读取了权限字段,并用strPurview来表示这个权限变量。这样就可以先写判断权限的函数了:500)this.width=500'>下面很重要,但很简单:// 判断拥有某种权限的函数public bool HasPurview(PersonPurview, purview){    if ( srtPurview.SubString(Covert.ToInt32(purview), 1) == "1" )    {        return true;        }    else    {        return false;    }}500)this.width=500'>这样就可用这个函数来判断了,比如相判断文章发表权(ArticleSend)if ( HasPurview(PersonPurview.ArticleSend) ){    ......;}怎么样,你看清楚了吧。500)this.width=500'>最后,我这还是设想,还没付诸行动。我也想把它作为一个问题供各位讨论,我觉得一个程序好的设计比好的代码更重要。 建议将字符类型的权限描述字段改为整型类型,这样在程序处理上更高效灵活些(见下面的范例)。   本文只是大概谈了一下粗粒度(表/模块级)的权限控制方案,对于细粒度(行/字段级)的数据控制解决办法,本人在回复中谈到了应用工作流来解决此问题的通用方案,有关工作流的内容相对复杂些,三言两语可能说不太明白,通常大家的应用需要更简单快速的解决办法,那么,我就来说说我在一个小项目中使用的办法吧,希望能对各位看官有所帮助。 用户表定义如下:(类SQL脚本) TUser (     UserName  nvarchar(50),  --主关键字     Password  varbinary(128), --帐户口令     Permission int        --权限字(二进制掩码) ) 业务表(譬如:销售订单) TSalesOrder (     OrderNo  varchar(30),  --主关键字(订单编号)     Creator  nvarchar(50),  --订单制单员(关联 TUser 表的 UserName 字段)     ... 其他业务字段 ... ) 权限字枚举定义(C#) [FlagsAttribute] public enum Permission {     Select = 1, //表示可“读取”所属于自己的数据的权力     Delete = 2, //表示可“删除”所属于自己的数据的权力     Insert = 4, //表示可“新增”所属于自己的数据的权力     Update = 8, //表示可“更改”所属于自己的数据的权力     AnySelect = 128, //表示可“读取”所属于其他用户的数据的权力     AnyDelete = 256, //表示可“删除”所属于其他用户的数据的权力     AnyInsert = 512, //表示可“新增”所属于其他用户的数据的权力     AnyUpdate = 1024 //表示可“更改”所属于其他用户的数据的权力 }   如果一个用户拥有修改属于自己数据的权力则该用户的 TUser.Permission 字段的值为:9(Select + Update),如果他还同时想拥有读取其他用户数据的权力,那么其 Permission 字段值应为:137(Select + Update + AnySelect),只需要使用这些枚举项定义的数值进行相加就可以得到你想要授权的范围。   那么如何判断某条记录是属于自己的还是别人的呢?以上面定义的“销售订单”表为例,只要判断 TSalesOrder.Creator 字段是否为当前登录用户的名称即可。具体处理方法可在数据层来做该数据检测过滤。下面给出一段中间数据层的读取“销售订单”方法的代码片断(C#) public class SalesOrder {   //获取或设置当前操作的用户名(登录用户名)   public string UserName   {     get{...}     set{...}   }   //获取或设置当前操作用户的权限字   public Permission UserPermission   {     get{...}     set{...}   }   //获取销售订单表   public DataSet Select()   {     //创建销售订单记录集对象(通常你可以使用其他更简便的方法得到这个数据集对象,譬如:使用XSD)     DataSet dataSet = new DataSet("SalesOrderDST");     //生成销售订单表     dataSet.Tables.Add("TSalesOrder");     //生成销售订单字段     dataSet.Tables["TSalesOrder"].Columns.Add("OrderNo", typeof(String));     dataSet.Tables["TSalesOrder"].Columns.Add("Creator", typeof(String));     //创建数据连接对象     using(SqlConnection connection = new SqlConnection(connectionString))     {       //创建命令对象       SqlCommand command = connection.CreateCommand();       command.CommandText = "Select * FROM dbo.TSalesOrder";       //打开数据连接       connection.Open();       //得到数据读取器对象       using(SqlDataReader reader = command.ExecuteReader())       {         int ordinalOrderNo = reader.GetOrdinal("OrderNo");         int ordinalCreator = reader.GetOrdinal("Creator");         string creator;         //依次读取数据库中该表的所有行         while(reader.Read())         {           //输入数据行所有者字段为空,则跳过           if(reader.IsDBNull(ordinalCreator))             continue;           //获得数据行所有者字段值           creator = reader.GetString(ordinalCreator);           //如果当前数据行所有者是当前登录用户,则必须判断当前登录用户的权限是否具有“读取”自己数据的权力;-或者-           //如果当前数据行所有者不是当前登录用户,则必须判断当前登录用户的权限是否具有“读取”他人数据的权力。           //如果权限验证通过则,将该行记录加入到要返回的 DataTable 对象中,否则跳过。           if((String.Compact(creator, UserName, true) == 0) && (UserPermission & Permission.Select) == Permission.Select) ||            (String.Compact(creator, UserName, true) != 0) && (UserPermission & Permission.AnySelect) == Permission.AnySelect))               dataSet.Tables["TSalesOrder"].Rows.Add(new object[]{reader.GetString(ordinalOrderNo), reader.GetString(ordinalCreator)});         }       }       //关闭数据读取器和数据连接对象       reader.Close();       connection.Close();     }     //返回销售订单记录集对象     return dataSet;   } }   这个解决方法还是有些粗糙,并且不够通用,如果你想要一个更强大和通用的数据控制解决方案,那么,我还是那句老话,请使用工作流机制!!!


阅读全文(1842) | 回复(0) | 编辑 | 精华
 



发表评论:
昵称:
密码:
主页:
标题:
验证码:  (不区分大小写,请仔细填写,输错需重写评论内容!)

站点首页 | 联系我们 | 博客注册 | 博客登陆

Sponsored By W3CHINA
W3CHINA Blog 0.8 Processed in 0.047 second(s), page refreshed 144753110 times.
《全国人大常委会关于维护互联网安全的决定》  《计算机信息网络国际联网安全保护管理办法》
苏ICP备05006046号