以上是一个简化版本关系图.
User:用户表,存放用户信息
Role:角色表,存放角色信息
UserInRole:用户角色映射表,存放用户和角色的对就关系,多对多,一个用户可以对应多个
角色,而不同的角色有一同的权限。
Permissions:权限表,不同的角色对应不同的权限。权限信息使用一个字段flag来表示,
好处是可以使用位运算来计算权限,缺点是用位标识的权限受理论值限制,如int理论上可以
标识31种不同的权限, 当然可以整加一个字段来弥补,ApplicationID标识不同的模块
Application:模块信息。
public enum Flag:long
{
View=1,
Edit=2,
Delete=4
}
特性[Flag]告诉编译器,当编译器看到Flag枚举时,它会充许你用|(or)操作符组合枚举值,
就像二的整数幂一样,
例如 Flag Administer=Flag.View|Flag.Edit|Flag.Delete;表示三种权限的组合。
基础知识:
位运算
枚举Flag
当编译器看到Flag枚举时,它会充许你用|(or)操作符组合枚举值,
就像二的整数幂一样,
例如 Flag Administer=Flag.View|Flag.Edit|Flag.Delete;
常用操作,检查是否存在
Flag administer=Flag.View|Flag.Edit|Flag.Delete;
public bool Check(Flag administer,Flag mask)
{
bool bReturn = false;
if ((administer & mask) == mask)
bReturn = true;
return bReturn;
}
调用 Check(administer,Flag.Edit)将返回true.
public Flag SetBit(Flag administer,Flag mask)
{
return administer |= mask;
}
administer |= mask;操作相当于 administer = administer |mask;
从枚举中减去一种状态
administer &=mask;
如 :
Flag administer=Flag.View|Flag.Edit|Flag.Delete;
如需要禁止删除权限.
administer &=Flag.Delete;
另外,标记为flag的枚举类型,可以不设置值
public enum Flag:long
{
View,
Edit,
Delete
}
如需要设置,按以下规律, View=1,Edit=2,Delete=4,Reply=8按2次方累加,为什么会这样?因为他使用二进制操作,
当你使用 View=1,Edit=2,Delete=3,Reply=4这样的值, Flag.Delete 包含的值是Flag.Delete还是View=1|Edit=2就无从检测了.
每个用户,可以属于不同的角色不同的角色分配不同的权限,计算所有解权的所有可能的权限组合,只要有充许的权限,那么该用户既获取该权限。
在CS系统中,Permissions表合用了二个字段来标识权限.
AllowMask,DenyMask 规责是Deny优先,也就是说当权限标记为Deny那么不论是否Allow一律禁止该用户进行此项操作。
另外,像论坛类的权限设计,仅仅一个ApplicationID字段是不够用的,因为每个版块都需要设置不同的权限,来控制权限的粒度,可在增加一张Permission表,ApplicationID修改为版块ID
这样,就可以针对不同的版块设置不同的权限
好了,接下的问题是怎么和.net自带的权限系统挂钩了。。
在asp.net系统中 ,HttpContext.Current.User实现了一个接口IPrincipal,IPrincipal包含了另一个接口Identity
我们在设计User类的时候继承此接口
public class User:IPrincipal
{
string username;
public string Username
{
get{return username;}
set{username=value;}
}
}
实现IPrincipal接口方法
public IIdentity Identity
{
get {
if (!string.IsNullOrEmpty(username))
_Identity = new GenericIdentity(username,"Forums");
return (IIdentity)_Identity;
}
}
public bool IsInRole(string role)
{
.....
}
怎样和asp.net挂钩呢,这里可以在登陆时做检查
if(HttpContext.Current!=null){
User u= Users.GetUser(name);
HttpContext.Current.User =u;
在使用时
User u = HttpContext.Current.User as User;
当然检查用户角色可以直接用
if(HttpContext.Current.User.Identity.IsAuthenticated&&HttpContext.Current.User.IsInRole(角色名))
另外可以直接把到当用户权限策略挂接到当前线程 ,使用以下方法
AppDomain.CurrentDomain.SetPrincipalPolicy(User);
好了,接下来,怎么check权限?
我倾向于使用Attribute
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Delegate, Inherited = true, AllowMultiple = true)]
public class CheckPermissionAttribute : Attribute
{
int appID;
public int ApplicationID
{
get { return appID; }
set { appID = value; }
}
Permission _allMask;
public Permission AllMask
{
get { return _allMask; }
set { _allMask = value; }
}
public CheckPermissionAttribute(ApplicationID app, Permission allMask)
{
appID = app;
_allMask = allMask;
}
public CheckPermissionAttribute(Permission allMask)
{
_allMask = allMask;
}
}
AttributeUsage 第一个参数表示该属性可以应用于类,方法,属性,代理上
Inherited 检查继承的权限。
AllowMultiple 充许多次应用。
按下来,设计一个基类,继承自Page:
public class PageBase : Page
{
Flag _allMask;
/// <summary>
/// 检查类型权限
/// </summary>
public void CheckClass()
{
Type type = this.GetType();
CheckPermissionAttribute att = (CheckPermissionAttribute)CheckPermissionAttribute.GetCustomAttribute(type, typeof(CheckPermissionAttribute));
if (att != null)
{
Check(att.AllMask);
}
}
/// <summary>
/// 检查函数调用权限
/// </summary>
/// <param name="methodName">方法名</param>
public void CheckMethod(string methodName)
{
Type type = this.GetType();
string name = "*";
if (!string.IsNullOrEmpty(methodName))
name = methodName;
MemberInfo[] mis = type.FindMembers(MemberTypes.Method ,BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.IgnoreCase,Type.FilterNameIgnoreCase,name);
foreach (MethodInfo m in mis)
{
CheckPermissionAttribute att = (CheckPermissionAttribute)CheckPermissionAttribute.GetCustomAttribute(m, typeof(CheckPermissionAttribute));
if (att != null)
{
Check(att.AllMask);
}
}
return;
}
public void Check(Flag permissions)
{
if (!CheckPermission(permissions))
{
string url = string.Format("MsgPage.aspx?msg={0}", HttpUtility.UrlEncode("您没有权限访问该资源"));
Response.Redirect(url);
}
}
public void Check(ApplicationID appID, Flag permissions)
{
PermissionManager pm= Spaces.PermissionManager.Instance(appType);
if (!CheckPermission(pm,permissions))
{
string url = string.Format("MsgPage.aspx?msg={0}", HttpUtility.UrlEncode("您没有权限访问该资源"));
Response.Redirect(url);
}
}
protected override void OnInit(EventArgs e)
{
CheckClass();
base.OnInit(e);
}
}
如何使用:
[CheckPermission(2, Flag.View)]
public partial class MyPage : PageBase
{
}
若没有查看权限,会自运导向错误页面。
在类上应用挺方便。
方法上应用我于一个方法比较麻烦,我还没有找到在页面class里怎么获取当前调用的类名.
可以调用 CheckMethod(方法名称);如
[CheckPermission(2, Flag.Delete)]
public partial class MyPage : PageBase
{
public void test()
{
CheckMethod("test");
.......
}
}
这还是需要重复劳动
评论
#1楼 116.30.36.* 2007-11-11 22:08 sunrise想 [未注册用户]
建议 Permissions:权限表,加一个user的字段 回复 引用
#2楼 2007-11-11 23:11 大石头
“方法上应用我于一个方法比较麻烦,我还没有找到在页面class里怎么获取当前调用的类名.”
如何获取调用者信息
http://www.nnhy.org/bbs/dispbbs.asp?boardID=4&ID=64&page=1 回复 引用 查看
#3楼 2007-11-11 23:12 大石头
相信应该能完美的解决你的问题^_^ 回复 引用 查看
#4楼 [楼主] 2007-11-12 00:42 潇笑
1楼:
加一个user的字段 就没必要了,UserInRole有映射表了,性能不错。
Permissions存放权限的,若要加user字段,那和基本角色表都可以去掉了,并且这样
Permissions需要存放大量的内容,因为每个用户都需要N行记录来记录权限设置。
TO 大石头:
这个方法太帅了,可以完美解决我的问题.谢谢
回复 引用 查看
#5楼 2007-11-12 08:32 大石头
呵呵,能帮上你的忙就好 回复 引用 查看
#6楼 123.253.111.* 2007-11-12 08:46 chill [未注册用户]
用这个,不如用membership呢.这个与membership基本上差不多,功能上还要差些.我还以为怎么是什么好的解决方案呢.
建议多看一看membership.此方案没有新的创意或亮点,如果是在1.1环境中,无话说. 回复 引用
#7楼 218.18.147.* 2007-11-12 09:36 sunrisex [未注册用户]
加一个user的字段
Permissions需要存放大量的内容,因为每个用户都需要N行记录来记录权限设置。?
假设我有一个用户属于某一个Role,但是只有某个功能他不能用(这个role的其他用户可以用),不知道你如何处理? 回复 引用
#8楼 2007-11-12 10:05 森林鸟
用户中途权限发生变化时,怎么办呢
比如,屏蔽他评论,而又没有相应的角色 回复 引用 查看
#9楼 [楼主] 2007-11-12 12:47 潇笑
--假设我有一个用户属于某一个Role,但是只有某个功能他不能用(这个role的其他用户可以用),不知道你如何处理?
如果Permissions时有用户字段,那么每个模块,每种权限都需要加一个userID
2. 我倾向于把这个功能的用户角色映射删除,当然可以像cs系统一样,加一个Deny字段,角色禁用的权限, 禁止优先。
to:chill membership是需要自已写要限的, 这可以挂membership
也就是实现一个接口, 在web.config更改Membership的Provider配置,二者没有冲突.Membership并没有权限配置部分,只有角色吧,因为这和你实际做的项目相关,当然,通用的membership粒度控制并没那么细。
to:森林鸟
我倾向于把用户从角色中移除,当然可以向cs那样加一个Deny字段
回复 引用 查看
#10楼 2007-11-12 13:50 Michael.li
写的不错,和我现在做的权限管理系统差不多.
关于怎么在方法上检测权限属性的,你可以查看我的开源ASP.NET通用权限管理系统(FrameWork).可以在http://framework.supesoft.com 下载 回复 引用 查看
#11楼 [楼主] 2007-11-12 14:26 潇笑
楼上的不错,界面不错 ,很好,很强大 这个我已经实现了
图:
http://xiaoxiao.bfor.cn/rbac.jpg
其实权限系统很难做到通用,否则microsoft早该集成了,membership 也只是做到角色这一级。因为不同系统之前权限差异很大. 回复 引用 查看
#12楼 2007-11-12 15:07 小庄
“方法上应用我于一个方法比较麻烦,我还没有找到在页面class里怎么获取当前调用的类名.”
我的做法是建立一个页面名称和模块对应的表,从页面url中取出页面名称,在找到对应的模块ID,就不用在判断权限的方法中需要模块ID参数了。
在实际应用中并不是每个模块的权限都是一样的(浏览,添加,修改,删除等),我更倾向于把每个模块都有多少种权限也写入数据库,甚至这个权限对应的方法名也写入,这样就可以在方法内部check(反射方法名)就OK了。 回复 引用 查看
#13楼 2007-11-12 16:04 静水≈深流
[CheckPermission(2, Flag.View)]
public partial class MyPage : PageBase
{
}
以前做毕业设计的时候也用这个方法的,不过现在不用了,每个页面里面要加这个方法,太累了,而且也不能够控制到按钮级的操作!也不能做到数据库动态配置和修改!
当这个页面加载的时候,你可以读到这个页面中的所有信息(页面名称,按钮名称等等)!通过这些信息去控制页面的访问权限!
比如这个页面MyPage.aspx加载的时候,数据库中配置该页面访问权限为12,页面中有个but_add按钮访问权限为 123,然后跟用户的权限去匹配,在去设置这个页面的可见度。 回复 引用 查看
#14楼 2007-11-12 16:06 静水≈深流
@小庄
我们的做法比较类似!回复后才看到你的留言。 回复 引用 查看
#15楼 [楼主] 2007-11-12 16:08 潇笑
server form 并没有模块参数,在方法里可以CheckMethod(方法名);
不过我的目标是无需入动方法体内的代码。只应用属性来配置权限
大石头 的方法经测试,完全可行。。 回复 引用 查看
#16楼 218.18.147.* 2007-11-12 17:51 sunriseyuen [未注册用户]
--假设我有一个用户属于某一个Role,但是只有某个功能他不能用(这个role的其他用户可以用),不知道你如何处理?
如果Permissions时有用户字段,那么每个模块,每种权限都需要加一个userID
为何要 每种权限都需要加一个userID ?
没有填User ID 就是 公用,填了userid 就是exception
2. 我倾向于把这个功能的用户角色映射删除,当然可以像cs系统一样,加一个Deny字段,角色禁用的权限, 禁止优先。
可能你没有明白我的意思
如
USER ROLE
A R1
B R1
C R1
D R1
...
ROLE FUNCTION
R1 F1
R1 F2
R1 F3
...
用户C的F3的功能不能用,其他的全部都能套用R1 的角色
回复 引用
#17楼 [楼主] 2007-11-12 19:13 潇笑
i see.其实那个那可以在配置一个Rx只包含F2,F2啊
当然我说了 另加deny这就是处理你这说的这种情况的
我就按你的说法解释一下
USER ROLE
A R1
B R1
C R1 +R2 //表示用户有R1,R2二种角色
D R1
ROLE PERMISSIONS
role allow deny
role:角色
allow:充许的权限
deny:禁用的权限
角色R1记录
role =R1
allow =F1|F2|F3;
deny =none;
--------------------------
角色R2记录
role =R2
allow =F1|F2|F3;
deny =F3;
用户A,B,C,D都属于角色R1,现在A,B,C,D分配了R1的角色,那么获取了F1,F2,F3功能,此时,你想把C用户的F3权限去除,那么只要在添加一个角
色R2就行了,因为这个规则中,禁止优先.
回复 引用 查看
#18楼 116.30.160.* 2007-11-12 20:13 sunrise想 [未注册用户]
--引用--------------------------------------------------
潇笑: i see.其实那个那可以在配置一个Rx只包含F2,F2啊
当然我说了 另加deny这就是处理你这说的这种情况的
我就按你的说法解释一下
USER ROLE
A R1
B R1
C R1 +R2 //表示用户有R1,R2二种角色
D R1
ROLE PERMISSIONS
role allow deny
role:角色
allow:充许的权限
deny:禁用的权限
角色R1记录
role =R1
allow =F1|F2|F3;
deny =none;
--------------------------
角色R2记录
role =R2
allow =F1|F2|F3;
deny =F3;
用户A,B,C,D都属于角色R1,现在A,B,C,D分配了R1的角色,那么获取了F1,F2,F3功能,此时,你想把C用户的F3权限去除,那么只要在添加一个角
色R2就行了,因为这个规则中,禁止优先.
--------------------------------------------------------
屏蔽F3是其中一个例子,还可以是另外加一个F8,等等,
回复 引用
#19楼 116.30.160.* 2007-11-12 20:15 sunriseyux [未注册用户]
--引用--------------------------------------------------
潇笑: i see.其实那个那可以在配置一个Rx只包含F2,F2啊
当然我说了 另加deny这就是处理你这说的这种情况的
我就按你的说法解释一下
USER ROLE
A R1
B R1
C R1 +R2 //表示用户有R1,R2二种角色
D R1
ROLE PERMISSIONS
role allow deny
role:角色
allow:充许的权限
deny:禁用的权限
角色R1记录
role =R1
allow =F1|F2|F3;
deny =none;
--------------------------
角色R2记录
role =R2
allow =F1|F2|F3;
deny =F3;
用户A,B,C,D都属于角色R1,现在A,B,C,D分配了R1的角色,那么获取了F1,F2,F3功能,此时,你想把C用户的F3权限去除,那么只要在添加一个角
色R2就行了,因为这个规则中,禁止优先.
--------------------------------------------------------
你这个R2 的情况可能有很多 R2 ... R10
回复 引用
#20楼 2007-11-13 09:18 Jeffery Huang
一个长久的话题,呵呵 回复 引用 查看
#21楼 222.43.20.* 2007-11-13 09:35 墙外行人 [未注册用户]
那如果用户的每一个角色,他们的数据库字段不一样,
如何设计用户数据库users表呀, 回复 引用
#22楼 [楼主] 2007-11-13 21:54 潇笑
UserInRole啊,一对多映射,这和数据类型无关 回复 引用 查看
#23楼 121.68.92.* 2007-11-14 11:35 ssjh [未注册用户]
google下azman
重复造轮子,无知 回复 引用
#24楼 [楼主] 2007-11-14 16:50 潇笑
这不是重复造轮子的,是怎么更合理利用轮子的问题.我承认自已无知. 回复 引用 查看
#25楼 2007-11-14 17:53 Jeffery Huang
楼主能不能把方法的权限检查补充一下?谢谢 回复 引用 查看
#26楼 221.137.234.* 2007-11-19 05:18 要名字的 [未注册用户]
sorry 这几天有点忙,整理中.. 回复 引用
#27楼 2007-12-05 09:45 代码乱了
@潇笑
server form 并没有模块参数,在方法里可以CheckMethod(方法名);
不过我的目标是无需入动方法体内的代码。只应用属性来配置权限
大石头 的方法经测试,完全可行。。
-------------------------------------------
大石头 的方法只能得到正在调用执行的方法,我觉得最终也需要在test方法中加上CheckMethod(),只是省略了方法名这个参数。;如果不需要方法内加这一句,不知道你是怎么解决的
回复 引用 查看
#28楼 2007-12-06 12:11 大石头
哈哈,楼上要的,其实就是AOP啦,你可以去找找相关资料。 回复 引用 查看
#29楼 2007-12-07 11:24 代码乱了
@大石头
AOP我倒是想到了,只是觉得在asp.net中用动态代理之类的AOP技术计影响很大啊,楼主目前已经用到了AOP? 回复 引用 查看
#30楼 219.144.166.* 2007-12-20 20:47 Lilybb [未注册用户]
ApplicationID标识不同的模块
请问是不是这样做的前提是必须在开发前确定模块,模块id值将在开发中用来确定权限。所以也必须先确定id。那么如果后期增加呢?
Application表的内容是直接输入到数据库中的 程序中无法编辑只能访问? 回复 引用
#31楼 222.91.147.* 2007-12-24 14:12 Lilybb [未注册用户]
在使用时
User u = HttpContext.Current.User as User;
为什么我用这个一直报错 回复 引用
#32楼 2008-01-10 11:06 陈思涵
最近也在研究这个东东,楼主能否贴出项目代码? 回复 引用 查看
#33楼 2008-01-14 11:01 ithurricane
正好要研究了
回复 引用 查看
#34楼 202.102.188.* 2008-02-12 16:32 本身就不用刷新 [未注册用户]
觉得累。
为什么不是
user
u_r
role
r_p
permission
u_p
三角关系?
回复 引用
#35楼 61.140.248.* 2009-01-16 19:46 崋 [未注册用户]
@chill
呵呵
我同意
不过我觉得直接用外键
直接调用 回复 引用
#36楼 2009-03-02 16:40 一线风
枚举要定义好,如果你提供编译后的DLL文件,别人想扩展的时候乍办? 现在正在想这个问题! 回复 引用 查看
#37楼 122.242.144.* 2009-05-02 23:05 时代网络 [未注册用户]
Attribute 里 public Permission AllMask , Permission 是自定义的? 还有 PermissionManager ,初学,通过你的代码还原成实例有些难度,学习中,还望指点下,谢谢。 回复 引用
#38楼 [楼主] 2009-05-03 20:14 潇笑
Permission 是权限枚举
PermissionManager 权限位运算,读出用户角色检查权限等操作 回复 引用 查看