简单扩展Shiro实现 类似
organization:create OR organization:update OR organization:delete
( organization:create Or organization:update ) OR NOT organization:delete
( organization:create && organization:update ) OR ! organization:delete
其中操作符不限大小写,支持and、or、not,以及&&、||、!
唯一缺点就是为了解析方便,所有内容必须用空格隔开
我先是看到了这篇博客:http://jinnianshilongnian.iteye.com/blog/1864800
然后觉得可以实现的更完善一些,突然想到可以用逆波兰表达式实现复杂一些的表达式解析
于是便有了这篇文章
实现思路:
1.将字符串分割成字符串集合
(类似: ( organization:create Or organization:update ) OR NOT organization:delete
就可以分割成[(, organization:create, Or, organization:update, ), OR, , NOT, organization:delete]这样的集合 )
2.然后将该集合转换为逆波兰表达式(此处将操作符做了忽略大小写的操作):
(上例就可以转换为:[organization:create, organization:update, or, organization:delete, not, or])
3.再将其中除了操作符以外的权限字符串,用shiro的验证方法转为true 或者是 false
(转换为:[false, true, or, true, not, or])
4.然后再求最终逆波兰表达式的值,大功告成!
上代码:
import org.apache.shiro.authc.credential.CredentialsMatcher; import org.apache.shiro.cache.CacheManager; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.*; /** * Created by KisChang on 15-8-10. * hasPermission支持复杂表达式(使用逆波兰表达式计算) */ public abstract class OperatorAuthorizingRealmWithRpn extends AuthorizingRealm { private static final Logger logger = LoggerFactory.getLogger(OperatorAuthorizingRealmWithRpn.class); //支持的运算符和运算符优先级 public static final Map<String, Integer> expMap = new HashMap<String, Integer>(){{ put("not",0); put("!" ,0); put("and",0); put("&&" ,0); put("or" ,0); put("||" ,0); put("(" ,1); put(")" ,1); }}; public static final Set<String> expList = expMap.keySet(); public OperatorAuthorizingRealmWithRpn() { } public OperatorAuthorizingRealmWithRpn(CacheManager cacheManager) { super(cacheManager); } public OperatorAuthorizingRealmWithRpn(CredentialsMatcher matcher) { super(matcher); } public OperatorAuthorizingRealmWithRpn(CacheManager cacheManager, CredentialsMatcher matcher) { super(cacheManager, matcher); } @Override public boolean isPermitted(PrincipalCollection principals, String permission) { Stack<String> exp = getExp(expList, permission); if (exp.size() == 1){ return super.isPermitted(principals, exp.pop()); } List<String> expTemp = new ArrayList<>(); //将其中的权限字符串解析成true , false for(String temp : exp){ if (expList.contains(temp)){ expTemp.add(temp); }else{ expTemp.add(Boolean.toString(super.isPermitted(principals, temp)) ); } } logger.debug("permission:{} Rpn:{} parse:{}", permission, exp, expTemp); //计算逆波兰 return computeRpn(expList, expTemp); } private static boolean computeRpn(Collection<String> expList,Collection<String> exp){ logger.debug("RPN exp :{}", exp); Stack<Boolean> stack = new Stack<>(); for(String temp : exp){ if (expList.contains(temp)){ if ("!".equals(temp) || "not".equals(temp)){ stack.push( !stack.pop() ); }else if ("and".equals(temp) || "&&".equals(temp)){ Boolean s1 = stack.pop(); Boolean s2 = stack.pop(); stack.push(s1 && s2); }else{ Boolean s1 = stack.pop(); Boolean s2 = stack.pop(); stack.push(s1 || s2); } }else{ stack.push(Boolean.parseBoolean(temp)); } } if (stack.size() > 1){ logger.error("computeRpn RESULT ERROR>{} exp:{}", stack, exp); throw new RuntimeException("compute error! stack: "+ exp.toString()); }else{ logger.debug("computeRpn RESULT SUCCESS>{}" , stack); return stack.pop(); } } //获得逆波兰表达式 private static Stack<String> getExp(Collection<String> expList, String exp) { Stack<String> s1 = new Stack<>(); Stack<String> s2 = new Stack<>(); for (String str : exp.split(" ")){ str = str.trim(); String strL = str.toLowerCase(); if ("".equals(str)){ continue; } if ("(".equals(str)){ //左括号 s1.push(str); }else if (")".equals(str)){ //右括号 while(!s1.empty()){ String temp = s1.pop(); if ("(".equals(temp)){ break; }else{ s2.push(temp); } } }else if(expList.contains(strL)){ //操作符 if (s1.empty()){ s1.push(strL); }else { String temp = s1.peek(); if ("(".equals(temp) || ")".equals(temp)){ s1.push(strL); }else if(expMap.get(strL) >= expMap.get(temp)){ s1.push(strL); }else{ s2.push(s1.pop()); s1.push(strL); } } }else{ //运算数 s2.push(str); } } while(!s1.empty()){ s2.push(s1.pop()); } return s2; } }
只要将自己实现的userRealm 改为继承自上面这个类,就可以实现了!
第一次写博客,有什么错误欢迎指出。另外代码可能写的也不算多好,或是暗含bug(至少我简单测试了一下没有问题),希望大家指正!