简单扩展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(至少我简单测试了一下没有问题),希望大家指正!