简单扩展shiro 实现NOT、AND、OR权限验证(支持复杂一点的表达式)

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

 

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值