角色权限,RBAC

角色权限,RBAC



 以上是一个简化版本关系图.
User:用户表,存放用户信息
Role:角色表,存放角色信息
UserInRole:用户角色映射表,存放用户和角色的对就关系,多对多,一个用户可以对应多个
角色,而不同的角色有一同的权限。
Permissions:权限表,不同的角色对应不同的权限。权限信息使用一个字段flag来表示,
好处是可以使用位运算来计算权限,缺点是用位标识的权限受理论值限制,如int理论上可以
标识31种不同的权限, 当然可以整加一个字段来弥补,ApplicationID标识不同的模块
Application:模块信息。

  [Flags]
     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");
   .......
  }
}

这还是需要重复劳动



 

posted on 2007-11-11 19:21 潇笑 阅读(5871) 评论(36)  编辑 收藏 网摘

评论

#1楼  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楼  2007-11-12 08:46 chill [未注册用户]

用这个,不如用membership呢.这个与membership基本上差不多,功能上还要差些.我还以为怎么是什么好的解决方案呢.
建议多看一看membership.此方案没有新的创意或亮点,如果是在1.1环境中,无话说.
  回复  引用    

#7楼  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楼  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楼  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就行了,因为这个规则中,禁止优先.

1. 代码生成器 有了数据库结构设计文档后,若直接按这个设计文档生成相应的代码,工作效率还是会显著提高,一方面可以从这个无聊的、没有什么创造性的工作里解放出来,另一方面生成好的代码质量也高一些,后期的维护也方便一些。 你想让10个人写出来的代码都像一个人一样还是想当的困难,若没有超强的管理能力甚至是可以说,不太可能看上去像一个人代码,各种风格都会有。 盖大楼先有图纸,再进行施工一样,做软件项目,还是先有设计后有代码比较好,修改设计上花费的代价远比修改代码上花费的成本会少很多。 可以根据Pdm数据库设计文档,直接生成非常专业的数据库字典, 1.1. 创建数据库PDM模型 根据实际业务需要,建立所需输入单据的数据库模型,采用PowerDesigner设计的PDM文件。 1.2. 代码批量生成 使用代码生成器批量生成前台后台代码。 1,选择设置好的PDM文件 2,选择输出目录 3,点击‘全部生成’,以批量生成代码 1.3. 部署所生成代码 1,把桌面上生成的DotNet.Business目录中的6个子目录全部复制到 DotNet.WebForm\App_Code\WorkFlow里面; 2,把桌面上生成的DotNet.Web目录中的6个子目录全部复制到 DotNet.WebForm\Modules\WorkFlow里面; 3,重新编译项目,登录查看所生成的页面。 1.4. 菜单调整设置 1,在数据库中运行代码生成器生成的各个数据库脚本,插入审批流程模板到审批流程表里面。 2,如果对菜单设置不满意,可以通过菜单管理来灵活设置。 1.5. 二次开发 生成的代码里面有比较完备的注释和示例代码,可根据需要增删。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值