权限管理 (一) 设计思路分析和实现受权、认证

  1. 权限的认识
    1. 提及对权限的理解能够说是从肤浅到了解,如今也只是处在了解的水平,先来看这样一个通俗且真实的故事:咱们都知道在有体制的公司或者机关单位,给人的体制化特别突出,通俗的说就是官大一级压死人,上级的权利每每要比下面人大,有什么事情了须要通过上级审批、赞成才可以执行,这也是一种法律程序,不少事情都须要走法律流程。
    2. 你没有在领导的位置上有些事情你说了就不算须要领导赞成,若是某天你的上司说“这件事你说了算,由你负责”这就是把管这事的权利交给你,叫受权;而后,和这件事打交道的人员须要确认谁管这事,这就叫作认证。权限也就是受权和认证的过程。
    3. 在作机房系统中一样也涉及到了权限,系统中分了三类人员管理员、操做员和通常用户,须要根据不一样类型的人员显示不一样的界面模块,当初实现的时候仅仅是在界面层作了一个简单的判断作的很是粗糙,只是简单的显示出人员对应的模块没有深刻的进行控制,如一个模块的增删改查控制,若是该人员对应的模块显示出来了就会有该模块的全部权限,不够灵活。
    4. 随着作的系统逐步变大、模块也增多了咱们引入了角色一词表示一组权限的集合,至关于根据实际需求把权限分了“类”分红一组一组的,能够将一组权限授予用户,如此一来,不须要一个个的授予用户变的方便了。固然也能够给某一个用户单独受权。
    5. 权限设计的目标是设计出这样的系统界面:用户受权、认证、模块动态显示等,这部分对于系统的安全性相当重要,所以,权限设计很是重要,也有一些成熟的模型解决权限管理,下面咱们来介绍一种实现解决方案:
  2. 实现思路和需求
    1. 把各个模块、角色、用户抽象出来各对应一个实体类,让用户或角色与模块关联并增长属性控制增删改查,这很像是一个M:N的对应关系又往中间一个关系上添加属性,受权的过程当中主要经过角色给用户受权,特殊的也能够给用户单独受权。
    1. 用户(User)能够拥有多个角色(Role),角色能够被分配给多个用户
    2. 权限的意思就是对某个资源的某个操做,如今规定:
      1. 所谓资源,即系统的模块
      2. 所谓操做,包括:增长、删除、修改、查询等操做
    3. 权限管理系统的整体功能分为:受权与认证
    4. 受权,指将权限授予角色或用户
      1. 若是用户A拥有角色B、角色C,那么,缺省的状况下,用户A将拥有被分配给角色A和角色C的全部权限(即默认状况下,用户A继承其拥有的角色所具备的全部权限)
      2. 若是用户拥有多个角色,那么用户的权限是这些角色权限的合集
      3. 若是用户拥有多个角色,并且角色之间的受权有冲突(好比对同一个资源的同一个操做,一个角色为“容许”,另一个角色为“不容许”),将以优先级别高的角色为准(所谓优先级别,也就是对于这个用户所拥有的角色而言,是有顺序的,同一个角色在不一样的用户那里可能拥有不一样的优先级)
      4. 除了能够对角色进行受权外,也能够针对用户进行受权,也就是说,将权限授予用户。针对某个资源的全部操做,咱们能够设置这些权限对用户来讲是“继承”或“不继承”
        1. 继承:意思是这些权限将使用其(即用户)所拥有的角色的权限,而不使用其(即用户)单独设置的权限
        2. 不继承:意思是这些权限将使用其单独设置的权限,而不使用其所拥有的角色的权限
    5. 认证,指用户访问资源的某些操做时,根据受权,判断是否容许用户的访问
      1. 在用户访问的时候,须要进行即时的判断(是否有权访问)
      2. 应该提供查询的功能,能够查询某个用户所拥有的全部权限
  3. 抽象实体类
    1. 为了实现生面需求,抽象出来了下面这些实体类,这张类图是主要的实体对象,能够实现权限控制。
    2. ACL
    3. 图中主要不易理解的是ACL类,该类表示访问控制列表(access control list),具备用户或角色与模块的关联关系以及它们的操做标识符,其中,操做标识符利用int的二进制表示,javaint32位能够用其中任意四位表明CRUD,为了方便一些使用最右边四位表示,表示是否有操做权限。
    4. 用户还能够从角色那里继承权限,咱们在ACL类中新增长一个int属性表示角色是否被继承,在UsersRoles关系中加入一个字段表明角色的优先级,解决不一样角色拥有相同模块时的冲突问题。
  4. 实现
    1. 受权的过程即把模块的访问权授予用户或者角色。
    1. 某一模块受权或者取消受权的代码
    1. 	public void setPermission(int permission,boolean yes){
      		int tmp =1;
      		
      		tmp=tmp << permission;
      		if (yes) {
      			aclState |= tmp;
      		}else {
      			aclState &= ~tmp;
      		}
      	}


    2. 例如setPermission(1,true)表示授予 read 权限,一个int temp = 1的临时变量, aclState为原始受权状态,tmp的二进制表示是: 00000000 00000000 0000000000000001,tmp左移1位获得read。temp = tmp < < 1; temp变成:00000000 00000000 00000000 00000010,而第二位表明的就是read权限。因此也能得出,左移0、一、二、3就能获得Create、Delete、Update、Select权限。
    3. 假设原始受权aclState=00000000 00000000 0000000000001010,当变量yes=true时,为受权,将temp与aclState求|运算,由于temp如今只有他要受权的位为1,求或运算后,aclState=0000000000000000 00000000 00001010,这样就受权成功.
    4. 当变量yes=false时,为取消受权,先将temp取反,即为1111111111111111 11111111 11111011,如今只有要取消权限的位为0,其他全为1,而后与aclState求&运算,则除了要取消权限的位变0,其他的都不变,即aclState=0000000000000000 00000000 00001010.
    1. 受权流程

      1. 	/**
        	 * 受权:对角色或者用户
        	 */
        	public void addOrUpdatePermission(String principalType, int principalSn,
        			int resourceSn, int permission, boolean yes) {
        		//根据主体表示和资源表示查找ACL实例
        		ACL acl=findACL(principalType, principalSn, resourceSn);
        		//若是存在则更新ACL实例,更新受权
        		if (acl!=null) {
        			acl.setPermission(permission, yes);
        			getHibernateTemplate().update(acl);
        			return;
        		}
        		//若是不存在则建立新的实例
        		if (acl==null) {
        			
        			acl=new ACL();
        			acl.setPrincipalType(principalType);
        			acl.setPrincipalSn(principalSn);
        			acl.setResourceSn(resourceSn);
        			acl.setPermission(permission, yes);
        			getHibernateTemplate().save(acl);
        		}
        	}


    1. 认证流程
      1. 	public int getPermission(int permission){
        		
        		if (aclTriState == 0xFFFFFFFF) {
        			return ACL_NEUTRAL;
        		}
        		
        		int tmp=1;
        		tmp=tmp << permission;
        		tmp &= aclState;
        		if (tmp !=0) {
        			return ACL_YES;
        		}
        		return ACL_NO;
        		
        	}



      2. 	public boolean hasPermission(int userId, int resourceSn, int permission) {
        
        		//查找对特定用户的受权
        		ACL acl=findACL(ACL.TYPE_USER, userId,resourceSn);
        		if (acl!=null) {
        			int yesOrNo=acl.getPermission(permission);
        		
        			//若是是肯定的受权
        			if (yesOrNo != ACL.ACL_NEUTRAL) {
        				return yesOrNo==ACL.ACL_YES ? true:false;
        			}
        		}
        		//继续查找用户的角色受权
        		String hql="select r.id from UsersRoles ur join ur.user u join ur.role r "+
        					"where u.id=? order by ur.orderNo";
        		
        		List aclIds=getHibernateTemplate().find(hql,userId);
        		//依照角色的优先级依次查找其受权
        		for (Iterator iter = aclIds.iterator(); iter.hasNext();) {
        			Integer rid = (Integer) iter.next();
        			acl=findACL(ACL.TYPE_ROLE,rid, resourceSn);
        			
        			//一旦发现受权,便可返回结果
        			if(acl!=null){
        				return acl.getPermission(permission) == ACL.ACL_YES ?true:false;
        			}
        		}
        		return false;
        	}


  5. 上面是从了解权限到一个权限模块的解决思路和设计步骤,在大小不一样的系统中能够适当的扩展和删减,这只是一种思路还有不少关于权限的解决方法好比RBAC等。
相关文章
相关标签/搜索