蛙蛙推荐:web下的授权简单解决方案

蛙蛙推荐:web下的授权简单解决方案

【permission】表结构

【id】 int identity(1,1) not null PK:权限编号,自增列

【title】narchar(50):权限名称

【parentid】 int:父类ID

【url】varchar(500):菜单的链接地址

【state】 int: 状态字段,0表示菜单,1是具体权限

 

身份验证

用户表或者角色表里加一个权限集【permissionSet】的字段,里面存放着用逗号隔开的权限ID,表示一个权限的集合,用户进行身份验证后把权限字符串保存在FormsAuthenticationTicket.UserData里,或者保存在Session["permission"]里。

 

利用菜单实现简单授权

问题:我们的后台管理一般都有导航系统,各种角色登陆后这个导航菜单显示的元素是不一样的,是根据权限来显示的。我们先解决这个问题。

一般来说是一个树型的菜单,我们用微软的IE WebControls Treeview来做导航,先定义一个方法,代码如下。

 

public void AddTree(string ParentID,TreeNode pNode,TreeView tvn)

{

     DataSet ds = this.GetSqlSet("SELECT * FROM permission ORDER BY id where state = 0");

     DataView dvTree = new DataView(ds.Tables[0]);

 

     //过滤ParentID,得到当前的所有子节点

     dvTree.RowFilter = "parentid ='"+ParentID+"'";

     foreach(DataRowView Row in dvTree)

     {

         TreeNode Node=new TreeNode() ;

         if(pNode == null)

         {  

              //添加根节点根节点不设置链接地址

              Node.ID=Row["id"].ToString();

              Node.Text = Row["title"].ToString();

 

              //判断用户是否有这个功能,然后添加这个菜单,

              if (Session["permission"].ToString().IndexOf(","+Row["id"].ToString()+",")!= -1)

                   tvn.Nodes.Add(Node);

             

              AddTree(Row["id"].ToString(), Node,tvn);    //再次递归

         }

         else

         {   //添加当前节点的子节点

              Node.ID=Row["id"].ToString();

              Node.Text = Row["title"].ToString();

              Node.NavigateUrl = Row["url"].ToString();

              Node.Target = "main";

             

              //授权检查

              if (Session["permission"].ToString().IndexOf(","+Row["id"].ToString()+",")!= -1)

                   pNode.Nodes.Add(Node);

 

 

              AddTree(Row["id"].ToString(),Node,tvn);     //再次递归

         }

     }

}

 

加载菜单用AddTree("0",null,this.Tree1)就可以了。

注意,如果你让用户没有一个大类菜单的访问权限,却让它有这个大类菜单的小类的访问权限,这个菜单也不能显示,因为递归不到那里去。

问题:现在不该显示的菜单是不显示了,但是不能防止用户直接在地址栏里输入路径访问页面,下面我们来解决这个问题

实现页面访问权限检查

 

在页面基类里声明两个受保护成员,如下

protected string pageAccess; //页面访问权限字符串

protected bool isChkAccess = false; //本页是否执行访问权限检查,默认不执行检查

在页面基类的Load事件里调用下面的方法

void chkAccess()

{

     if (Session["permission"].ToString().IndexOf(","+this.pageAccess+",") == -1 && this.isChkAccess)

     {

         Response.Write("你没有访问该页的权限");

         Response.End();

     }

}

让所有的页面继承自你写的自定义页面基类,并在页面的构造函数里启用页面访问权限检查并设置访问权限ID,代码如下

this.isChkAccess = true; //启用页面权限检查

this.pageAccess = "6"; //设置页面访问权限字符串为6,实际上是黄页信息管理权限。

 

问题:现在恶意用户直接敲击地址也不能访问不该访问的页面了,但是有时候一个页面上不同的角色呈现的页面也是不一样的,比如同样的黄页管理页面,有些人可以审核,有些人不能审核,我们要动态的判断是否显示审核黄页的按钮

 

实现细粒度授权检查

 

还是在页面基类里添加一个方法,用来执行权限的断言。代码如下

public bool chkPermission(string permission)

{

     string permissionid = this.GetSqlScalar("select id from permission where state = 1 and title = '"+permission+"'").ToString();

     return Session["permission"].ToString().IndexOf(","+permissionid+",") != -1;

 

}

其中GetSqlScalar也是页面基类里定义的一个快速执行数据库操作的方法,然后你可以在具体的页面的LOAD方法里动态的设置某个UI是否显示。

Trace.Write("检查审核黄页的权限",this.chkPermission("审核黄页").ToString());

this.Button1.Visible = this.chkPermission("审核黄页");

 

上面的Button1就是审核黄页的按钮。

 

不足之处:

1、这里没有考虑用户,角色,权限集等概念,只是做了一些授权的简单实现,没有提供完整的RBAC方案。

2、这里的资源继承关系没有考虑进去,也就是子菜单是否继承父菜单的访问许可设置。

3、这里没有定义黑名单和白名单的高级授权内容。

4、这里没有显式定义每个资源的允许和拒绝权限,以及权限的计算策略。

5、这里没有考虑一个用户同时属于多个角色时的权限检查策略。

6、这里没有考虑用户的部门概念,关于组织机构概念,也没有提到,尽管这是权限系统设计的必要因素。

 

小节:有了这个接口后,再做一个用户,角色,权限的管理界面就可以了,这些的管理的都是对数据库的添加,修改,删除操作,比较简单了。主要是把权限集绑定到角色,(多对多)然后把用户绑定到角色(多对多),剩下的,就是用户登陆的时候根据角色这个链接表,读取它能获取哪些权限集,把这些权限集合并起来,就是这个用户的权限集了。

 

 

posted @ 2006-04-05 15:43  蛙蛙王子  Views(2556)  Comments(11Edit  收藏  举报