【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、这里没有考虑用户的部门概念,关于组织机构概念,也没有提到,尽管这是权限系统设计的必要因素。
小节:有了这个接口后,再做一个用户,角色,权限的管理界面就可以了,这些的管理的都是对数据库的添加,修改,删除操作,比较简单了。主要是把权限集绑定到角色,(多对多)然后把用户绑定到角色(多对多),剩下的,就是用户登陆的时候根据角色这个链接表,读取它能获取哪些权限集,把这些权限集合并起来,就是这个用户的权限集了。
http://onlytiancai.cnblogs.com/archive/2006/04/05/367582.html