做权限,表的结构我觉得大家都差不多了吧?模块表、用户表、用户组表、用户组权限表,这四个表,肯定都是固定了的,一个用户可以属于多个用户组,一个用户组可以拥有多个权限(就是可以访问多个模块)
有时候还会再加一个用户所属性用户组表,这个表也有人会在用户表里加一个字段记录用户所属用户组的全部用户组编号的组合字符串
模块表记录系统里有几个模块,简单的系统可以把表结构设置得简单一点,只记录编号和描述就可以了,复杂的系统可以把表结构设置得复杂一些,像树形的结构,有上下级的关系
用户表记录所有可以登录的人员
用户组表实际上就是权限组表,把人员分类,按组赋给权限
用户组权限表,就是把用户组和模块对应起来,记录下每个用户组可以访问的权限是什么,这个表一般有两种结构:1、用户组编号、模块编号、是否有权;2、用户组编号、所有有权访问的模块的模块编号的组合字符串
对这些表怎么管理,这里不想管了,这个博客(user1/9/archives/2007/3551.html)写的这种方法就是用在复杂的权限表的管理的,用TreeView显示有上下级关系的模块关系
刚刚开始做OA的时候,发过一个帖子(http://www.tiantiansoft.com/bbs/dispbbs.asp?boardid=40&id=149555),我在做OA时,最后使用的方法很复杂,复杂到自己都描述不清楚,我用的是static DataTable,同样写了一个类来管理所有权限,每个页面的权限判断,主要是通过这个页面的地址检索DataTable里的记录判断的
下面开始讲利用static的Dictionary做权限的管理,嗯,我可能会说得很乱,就是,如果现在不记下来,明天我记不下这个东西了,现在很想去玩了,就是,一定要记下来,我一点一点说
1、我没有使用页面的地址,检索权限,我直接给页面赋了一个页面编号值
不知道你们还记不记得在做ASP的时候,权限,经常是在页面的一开始时赋给一个页面编号,再通过这个编号取值的,这样做有点像用pb写软件的时候先给每个window赋值一样
这种做法的优点是:省了通过地址取模块编号这一步,还有就是如果系统里使用了UrlRewritingNet重写url,也可以不用害怕取不到原本的地址
这种做法的缺点是:增加了开发时的劳动,每添加一个页面,就要自己把这个页面的编号写正确
这种做法要注意的是页面的执行顺序(user1/9/archives/2007/4020.html),如果使用了母版,执行顺序是这样的:ContentPage.PreInit - > Master.Init -> ContentPage.Init
我们要在Master.Init事件里判断用户权限,所以要在ContentPage.PreInit事件里对页面赋值
还有,为了把每个页面的操作减少到最小,尽量把重复的代码写在母版里,所以把页面编号这个属性,也设置在母版里,这样,为了在每个页面都可以调用到这个属性,就需要用到MasterType强化Mater类
这个是母版页中的内容
#region 检查权限
private string pageCode = "";
public string PageCode
{
set { pageCode = value; }
get { return pageCode; }
}
protected void Page_Init(object sender, EventArgs e)
{
if (Session["gcs"] == null)
{
Response.Write("<p>还没登录,请<a href=\"Login.aspx\">登录</a>");
Response.End();
}
if (AccessControl.GetAccess(Session["gcs"].ToString(), pageCode))
{
Response.Write("<p>有权访问</p>");
}
else
{
Response.Write("<p>没有权访问</p>");
//Response.End();
}
}
在每个页面的aspx文件中设置
<%@ MasterType VirtualPath="~/MasterPage.master" %>
在每个页面的.aspx.cs文件中设置
protected void Page_PreInit(object sender, EventArgs e)
{
Master.PageCode = "02";
}
2、编写权限控制类,因为这个类里用到的成员和方法都可以算是全局的,所以我使用了static,构造函数也使用了静态构造函数
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Collections.Generic;
/// <summary>
/// AccessControl 的摘要说明
/// </summary>
public class AccessControl
{
#region 三个静态成员:模块字典、用户组字典、用户组权限字典
static Dictionary<string, string> module = new Dictionary<string, string>();
static Dictionary<int, string> group = new Dictionary<int, string>();
static Dictionary<int, string> access = new Dictionary<int, string>();
public Dictionary<string, string> Module
{
get { return module; }
set { module = value; }
}
public Dictionary<int, string> Group
{
get { return group; }
set { group = value; }
}
public Dictionary<int, string> Access
{
get { return access; }
set { access = value; }
}
#endregion
#region 静态构造函数
static AccessControl()
{
//HttpContext.Current.Response.Write("<p>初始化数据</p>");
module.Add("01", "首页");
module.Add("02", "浏览");
module.Add("03", "添加");
module.Add("04", "修改");
module.Add("05", "删除");
group.Add(1, "管理员组");
group.Add(2, "录入组");
group.Add(3, "阅读组");
access.Add(1, "|01|02|03|04|05|"); //管理员组权限
access.Add(2, "|01|02|03|"); //录入组权限
access.Add(3, "|01|02|"); //阅读组权限
}
#endregion
#region 检查编号是groupCode的用户组,有没有编号是moduleCode的权限
public static bool GetAccess(string groupCodeStr, string moduleCode)
{
string[] groupCodeArray;
groupCodeArray = groupCodeStr.Split('|');
foreach (string groupCode in groupCodeArray)
{
if (groupCode == null || groupCode.Equals(""))
{
continue;
}
try
{
if (GetAccess(Convert.ToInt32(groupCode), moduleCode))
{
return true;
}
}
catch (FormatException)
{ }
}
return false;
}
public static bool GetAccess(int groupCode, string moduleCode)
{
try
{
//HttpContext.Current.Response.Write("<p>groupCode = " + groupCode.ToString() + ",moduleCode = " + moduleCode + ",access[" + groupCode.ToString() + "] = " + access[groupCode]);
moduleCode = "|" + moduleCode + "|";
if (access[groupCode].IndexOf(moduleCode) > -1)
{
return true;
}
}
catch (KeyNotFoundException)
{
//默认没有设置到模块字典里的页面,是全体可以访问的页面
//HttpContext.Current.Response.Write("<p>没有对应值:groupCode=" + groupCode.ToString() + ",moduleCode=" + moduleCode);
return true;
}
return false;
}
#endregion
}
这里面我全部都是直接添加的测试内容,如果是正式用在系统里,可以把静态构造函数修改一下,从数据库中读取对应的数据添加就可以了
解决一下三个成员:
module,模块字典,key是模块编号,value是模块描述
group:用户组字典,key是用户组编号,value是用户组描述
access:用户组权限字典,key是用户组编号,value是这个用户组可以访问的所有模块的模块编号的组合字符串
他们通过三个索引器管理,这三个索引器现在只是用在Login.aspx页面中测试,如果是正式的系统,静态构造函数是从数据库中读取的数据,这样,每当我们修改过数据库中的数据,我们就需要使用索引器重新写这三个成员的值了
我觉得没有别的要记录了,给出我测试的例子:UploadFiles/2007-8/825715849.rar
WebConfig文件中自定义页面现在设置成Off了,如果有需要,可以改成On