布尔代数和权限管理

一、布尔代数   

 布尔代数相信大家都接触过,根据维基百科的定义:

        The basic operations of Boolean algebra are the following ones:

  • And (conjunction), denotedxy (sometimesx ANDy or Kxy), satisfiesxy = 1 ifx = y = 1 and xy = 0 otherwise.
  • Or (disjunction), denotedxy (sometimesx ORy or Axy), satisfiesxy = 0 if x = y = 0 and xy = 1 otherwise.
  • Not (negation), denoted ¬x (sometimes NOTx, Nx or !x), satisfies ¬x = 0 ifx = 1 and ¬x = 1 ifx = 0.  

图片表示这几个运算的关系如下:

File:Vennandornot.svg

 

二、布尔代数的应用——位运算

        平时常用的逻辑二元运算(&&,||)是用布尔代数的最常见应用之一,还有就是这里要介绍的位运算:

        位运算就是二进制数值按照位运算符&(与),|(或),~(取反),^(异或),>>(左移),<<(右移)对二进制数值进行运算。比如,7的二进制是111,11的二进制是1011,那么7&11的结果就是二进制的11,即 3 ;7|11结果就是二进制的1111,即 15 ;1<<2 就是 二进制的100 ,即 4。

        而位运算也可以解决权限问题,如Linux系统中的文件权限设定,文件或目录的权限分为三种:”可读”、”可写”和”可执行”,分别用 1 、2 、4 来表示。有“可读”且“可写”权限就用 1+2,即3来表示。

        设原有权限为a,在a的基础上增加写权限可用运算a|2来实现,如1|2=3;类似的,减少写权限可以用 a&(~2)来实现,如3&(~2)=1。

        位运算的运算对象是二进制的位,速度很快,效率很高,而且节省空间,位运算做权限控制又相当地灵活。对与我日常使用的java来说,用Long类型来实现,可以实现64种权限的控制,如果嫌不够多,还可以用java.math.BigInteger来实现。

三、位运算权限管理的简单实现

        1.需求目前有 列表、只读、浏览、编辑、创建、共享这五个权限。可以用java的枚举实现对这几个权限的设定(AuthorityType):
public enum AuthorityType {
	EDIT(1L<<0),	//编辑
	READONLY(1L<<1), //只读
	READ(1L<<2),	//浏览
	LIST(1L<<3),	//列表
	CREATE(1L<<4),	//创建
	PSHARE(1L<<5);	//共享

	private Long type;
	private AuthorityType(Long type){
		this.type=type;
	}
	public Long getType() {
		return type;
	}
	public static Long getAllType() {
		Long l = 0L;
		for(AuthorityType a : AuthorityType.values()){
			l+=a.getType();
		}
		return l;
		
	}
}
        2.权限判断、设定、取消的代码实现(DocAuthority):
public class DocAuthority {
 
	/*当前具有的权限*/
    private Long            authority;
    
    private DocAuthority 	docauth;

    /*判断是否有编辑权限*/
    public boolean isEdit() {
		return (authority & AuthorityType.EDIT.getType()) >= AuthorityType.EDIT.getType();
	}
    /*设定编辑权限*/
	public DocAuthority setEdit(DocAuthority docauth) {
		this.authority = authority|AuthorityType.EDIT.getType();
		docauth.authority = authority;
		return this.docauth = docauth;
	}
	/*取消编辑权限*/
	public DocAuthority removeEdit(DocAuthority docauth) {
		this.authority = authority&(~AuthorityType.EDIT.getType());
		docauth.authority = authority;
		return this.docauth = docauth;
	}
	/*判断是否有只读权限*/
	public boolean isReadOnly() {
		return (authority & AuthorityType.READONLY.getType()) >= AuthorityType.READONLY.getType();
	}
	/*设定只读权限*/
	public DocAuthority setReadOnly(DocAuthority docauth) {
		this.authority = authority|AuthorityType.READONLY.getType();
		docauth.authority = authority;
		return this.docauth = docauth;
	}
	/*取消只读权限*/
	public DocAuthority removeReadOnly(DocAuthority docauth) {
		this.authority = authority&(~AuthorityType.READONLY.getType());
		docauth.authority = authority;
		return this.docauth = docauth;
	}
	/*判断是否有浏览权限*/
	public boolean isRead() {
		return (authority & AuthorityType.READ.getType()) >= AuthorityType.READ.getType();
	}
	/*设定浏览权限*/
	public DocAuthority setRead(DocAuthority docauth) {
		this.authority = authority|AuthorityType.READ.getType();
		docauth.authority = authority;
		return this.docauth = docauth;
	}
	/*取消浏览权限*/
	public DocAuthority removeRead(DocAuthority docauth) {
		this.authority = authority&(~AuthorityType.READ.getType());
		docauth.authority = authority;
		return this.docauth = docauth;
	}
	/*判断是否有列表权限*/
	public boolean isList() {
		return (authority & AuthorityType.LIST.getType()) >= AuthorityType.LIST.getType();
	}
	/*设定列表权限*/
	public DocAuthority setList(DocAuthority docauth) {
		this.authority = authority|AuthorityType.LIST.getType();
		docauth.authority = authority;
		return this.docauth = docauth;
	}
	/*取消列表权限*/
	public DocAuthority removeList(DocAuthority docauth) {
		this.authority = authority&(~AuthorityType.LIST.getType());
		docauth.authority = authority;
		return this.docauth = docauth;
	}
	/*判断是否有创建权限*/
	public boolean isCreate() {
		return (authority & AuthorityType.CREATE.getType()) >= AuthorityType.CREATE.getType();
	}
	/*设定创建权限*/
	public DocAuthority setCreate(DocAuthority docauth) {
		this.authority = authority|AuthorityType.CREATE.getType();
		docauth.authority = authority;
		return this.docauth = docauth;
	}
	/*取消创建权限*/
	public DocAuthority removeCreate(DocAuthority docauth) {
		this.authority = authority&(~AuthorityType.CREATE.getType());
		docauth.authority = authority;
		return this.docauth = docauth;
	}
	/*判断是否有共享权限*/
	public boolean isPShare() {
		return (authority & AuthorityType.PSHARE.getType()) >= AuthorityType.PSHARE.getType();
	}
	/*设定共享权限*/
	public DocAuthority setPShare(DocAuthority docauth) {
		this.authority = authority|AuthorityType.PSHARE.getType();
		docauth.authority = authority;
		return this.docauth = docauth;
	}
	/*取消共享权限*/
	public DocAuthority removePShare(DocAuthority docauth) {
		this.authority = authority&(~AuthorityType.PSHARE.getType());
		docauth.authority = authority;
		return this.docauth = docauth;
	}
	/*判断是否有某些权限*/
	public boolean isSomeAuth(Long l) {
		return (authority & l) >= l;
	}
	/**设定某些权限
	 * @param DocAuthority
	 * @param Long a number less than Long.MAX_VALUE,but bigger than 0
	 * */
	public DocAuthority setAuth(DocAuthority docauth,Long l) {
		if((1<<63)-1>=l){
			this.authority = authority|l;
			docauth.authority = authority;
		}
		return this.docauth = docauth;
	}
	/**取消某些权限
	 * @param DocAuthority
	 * @param Long a number less than Long.MAX_VALUE,but bigger than 0
	 * */
	public DocAuthority removeAuth(DocAuthority docauth,Long l) {
		if(Long.MAX_VALUE>l){
			this.authority = authority&(~l);
			docauth.authority = authority;
		}
		return this.docauth = docauth;
	}
	public DocAuthority getDocauth() {
		return docauth;
	}
	
	public long getAuthority() {
		return authority;
	}
	
	public String toString(){
		return authority.toString();
		
	}

}

        3.对取出数据的权限进行增删操作(改也是由增删权限组成的):
DocAuthority d = new DocAuthority();
		//设定编辑权限
		d.setEdit(d);
		print(d);
		//取消编辑权限
		d.removeEdit(d);
		System.out.println(d.getAuthority());
		//设定只读和编辑权限
		d.setEdit(d).setReadOnly(d);
		print(d);
		//设定列表权限
		d.setList(d);
		System.out.println(d.getAuthority());
		//取消只读和编辑权限
		d.removeEdit(d).removeReadOnly(d);
		print(d);
		//设定全部权限
		d.setAuth(d, AuthorityType.EDIT.getType() + AuthorityType.CREATE.getType() + AuthorityType.READ.getType() + AuthorityType.READONLY.getType() + AuthorityType.LIST.getType() + AuthorityType.PSHARE.getType());
		System.out.println(d.getAuthority());
		//取消全部权限
		d.removeAuth(d, AuthorityType.getAllType());
		print(d);
		//设定全部权限
		d.setAuth(d, AuthorityType.getAllType());
		System.out.println(d.getAuthority());
		//是否具有某些权限,如编辑和创建权限
		d.isSomeAuth(AuthorityType.EDIT.getType() + AuthorityType.CREATE.getType());
		System.out.println(d.isSomeAuth(AuthorityType.EDIT.getType() + AuthorityType.CREATE.getType()));

四、位运算权限管理进阶

       1. 第三节实现了权限的增删改,而查询一般都是查数据库,目前常见的数据库都是支持位运算的(orace,mysql,sqlserver,postgresql等)。但是Hibernate却偏偏不支持位运算。那我们要如何达到自己的目的呢?
        常见的方式是利用Hibernate支持的自定义SQLFunction,定义一个bitand(a,b)的SQLFunction,最后配置自定义方言的Dialect并配置到hibernate配置文件。以mysql为例(参考:https://forum.hibernate.org/viewtopic.php?t=940978)
        1).实现一个自定义的SQLFunction:
public class BitAndFunction implements SQLFunction {
	public Type getReturnType(Type type, Mapping mapping) {
		return Hibernate.INTEGER;
	}

	public boolean hasArguments() {
		return true;
	}

	public boolean hasParenthesesIfNoArguments() {
		return true;
	}

	public String render(List args, SessionFactoryImplementor factory)
			throws QueryException {
		if (args.size() != 2) {
			throw new IllegalArgumentException(
					"BitAndFunction requires 2 arguments!");
		}
		return args.get(0).toString() + " & " + args.get(1).toString();
	}

}
        2).创建自己的方言 BitwiseAndSqlDialect:
public class BitwiseAndSqlDialect extends MySQLInnoDBDialect {

	public BitwiseAndSqlDialect() {
		  super();
		  registerFunction("bitand", new BitAndFunction());
		 }
	
}
        3).注册自己的方言:
hibernate.dialect=dbtest.sql.BitwiseAndSqlDialect
        4)创建一个简单的po:
public class DocAclPo implements Serializable {
	
	private static final long serialVersionUID = 1L;
	
	private Long id; 
	
	private Long numId;

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public Long getNumId() {
		return numId;
	}

	public void setNumId(Long numId) {
		this.numId = numId;
	} 

}
        5).设置一个简单的查询:
try {
	tx = session.beginTransaction(); // 开始一个事务
	//bitand
	//数据库查询 比如:具有列表和创建权限(对应8和16)的数据
 	Long authEdit = AuthorityType.LIST.getType();
 	Long authCreate = AuthorityType.CREATE.getType();
	Query query = session.createQuery("from DocAclPo as c where bitand(c.numId,"+(complexauth)+")>="+complexauth+" order by c.id");
	List<DocAclPo> list = query1.list();
	for(int i=0 ; i<list.size(); i++){
		System.out.println("DocAclPo.id is "+ list.get(i).getId()+" , DocAclPo.numId is "+list.get(i).getNumId());
	}
	tx.commit(); // 提交事务

结果为:

Hibernate: select docaclpo0_.id as id0_, docaclpo0_.num_id as num2_0_ from doc_acl docaclpo0_ where docaclpo0_.num_id & 24>=24 order by docaclpo0_.id
DocAclPo.id is 1 , DocAclPo.numId is 63
DocAclPo.id is 8 , DocAclPo.numId is 24

测试数据库中所有记录为:

1	63
2	19
3	20
4	18
5	21
6	22
7	23
8	24
9	32
        6).缺点:

        这么做可以满足需求,但是总体来说变化较大,因为使用了自己定义的数据库方言和函数,如果系统不止应用在常用的数据库(oracle.mysql,sqlserver,postgresql等),而是用在不支持位运算的数据库中,或者应用支持的数据库较多,要写N多种方言,再或者项目要求不能用自己的方言,那就不好处理了。

       下面提出一种新的方案——使用HQL支持的MOD方法来操作。来先看例子,再看原理。

        2.MOD方案
        1).创建一个DocAuthUtil拼装查询字符串:
public class DocAuthUtil {
	
	/*权限字符串拼接方法AND*/
	public static String setSearchAnd(String authority , AuthorityType ... authType){
		StringBuffer sb= new StringBuffer();
		for (AuthorityType a : authType){ 
			Long at = a.getType();
			sb.append("MOD("+ authority + "," +(at<<1)+")>="+at+" and ");
		}
		return "("+sb.substring(0, sb.length()-5)+")";
		
	}
	
	/*权限字符串拼接方法OR*/
	public static String setSearchOr(String authority , AuthorityType ... authType){
		StringBuffer sb= new StringBuffer();
		for (AuthorityType a : authType){ 
			Long at = a.getType();
			sb.append("MOD("+ authority + "," +(at<<1)+")>="+at+" or ");
		}
		return "("+sb.substring(0, sb.length()-4)+")";
		
	}
	
}
       2).设定测试查询语句:
	tx = session.beginTransaction(); // 查询具有列表和创建权限的数据
 	String qString ="from DocAclPo as c where 1=1 and"+DocAuthUtil.setSearchAnd("c.numId", AuthorityType.LIST,AuthorityType.CREATE)+" order by c.id";
	Query query1 = session.createQuery(qString);
	List<DocAclPo> list = query.list();
	for(int i=0 ; i<list.size(); i++){
		System.out.println("DocAclPo.id is "+ list.get(i).getId()+" , DocAclPo.numId is "+list.get(i).getNumId());
	}
	tx.commit(); // 提交事务

结果为:

Hibernate: select docaclpo0_.id as id0_, docaclpo0_.num_id as num2_0_ from doc_acl docaclpo0_ where 1=1 and mod(docaclpo0_.num_id, 16)>=8 and mod(docaclpo0_.num_id, 32)>=16 order by docaclpo0_.id
DocAclPo.id is 1 , DocAclPo.numId is 63
DocAclPo.id is 8 , DocAclPo.numId is 24
        3).解释:

       根据util中的设定,查询的权限值都是AuthorityType中的枚举,而AuthorityType的值都是2的整次幂,如1、2、4、8、16、32。假设实际具有的权限auth中数值最大的一个为32,如果查询权限32+16+4=52中是否有8 ,就可以用52÷(8×2)的余数是否大于等于8判断即可。因为auth的最大值为32+16+8+4+2+1总值为 2^6-1=63<32×2,16+8+4+2+1=2^5-1=31<16×2,所以at<<1 再取模后,必然小于等于at,且只当包含at时才有等于成立。因此算法中用了 "MOD("+ authority + "," +(at<<1)+")>="+at这种简单算法。

        4).优点:

        杜绝了方案1中的自定义函数和更改默认方言的不便;使用Hibernate中的预置函数MOD,可以使用数据库中对应字段的索引,不需要进行多数据库方言的开发等。

 

五、总结

        1.布尔代数非常简单明了,但是却可以用来做一些很有用的事情,比如逻辑判断,比如索引,比如这里的位运算判定权限。

        2.在特定的情况下,解决问题的方式不止一种,就如这里的MOD方式,在一定程度上可以替换位运算。知识是重要,但结合实际运用知识更重要。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值