文档章节

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

KisChang
 KisChang
发布于 2015/08/10 16:52
字数 813
阅读 923
收藏 9

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

  

© 著作权归作者所有

KisChang
粉丝 2
博文 2
码字总数 1799
作品 0
北京
后端工程师
私信 提问
加载中

评论(1)

火炎焱燚的贰阳程序猿
火炎焱燚的贰阳程序猿
非常有用,感谢分享
简单shiro扩展实现NOT、AND、OR权限验证

使用过shiro的朋友应该都知道在要想实现any permission的验证是比较麻烦。 很多朋友刚开始接触时以为如<shiro:hasPermission name="showcase:tree:"> 代表验证拥有任何权限,但这是错误的。如...

zh119893
2014/06/27
257
0
Apache Shiro 使用手册(三)Shiro 授权

一、授权的三要素 授权有着三个核心元素:权限、角色和用户。 权限 权限是Apache Shiro安全机制最核心的元素。它在应用程序中明确声明了被允许的行为和表现。一个格式良好好的权限声明可以清...

heroShane
2014/02/10
293
0
shiro 授权介绍

一、授权的三要素 授权有着三个核心元素:权限、角色和用户。 权限 权限是Apache Shiro安全机制最核心的元素。它在应用程序中明确声明了被允许的行为和表现。一个格式良好好的权限声明可以清...

kevin728971010
2016/11/07
58
0
周立/light-security

Light Security Light Security是一款基于 的、简洁而不简单的权限控制框架,可与 配合使用,支持 及 。 地址 GitHub Gitee 特点 优点 上手快速 开箱即用 轻量级,代码精简,不到500行代码;...

周立
05/07
0
0
第一章 Shiro简介——《跟我学Shiro》

1.1 简介 Apache Shiro是Java的一个安全框架。目前,使用Apache Shiro的人越来越多,因为它相当简单,对比Spring Security,可能没有Spring Security做的功能强大,但是在实际工作时可能并不...

真爱2015
2015/12/15
223
0

没有更多内容

加载失败,请刷新页面

加载更多

db.properties

jdbc.driver=com.mysql.cj.jdbc.Driverjdbc.url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT%2B8&characterEncoding=utf8&useSSL=falsejdbc.username=rootjdbc.pas......

行者终成事
15分钟前
3
0
从 Storm 到 Flink,汽车之家基于 Flink 的实时 SQL 平台设计思路与实践

在 2019 年之前,之家的大部分实时业务都是运行在 Storm 之上的。Storm 作为早期主流的实时计算引擎,凭借简单的 Spout 和 Bolt 编程模型以及集群本身的稳定性,俘获了大批用户。下图是实时计...

阿里云官方博客
16分钟前
4
0
网络安全-Day04

2.1.1 什么是SQL注入 2.1.2 注入分类 2.1.3 MySQL手工注入 2.1.4 sqlmap使用 2.1.5 总结 什么是SQL注入 SQL注入漏洞原理 什么是SQL 结构化查询语言(Structured Query Language),是一种特殊目...

Jerry1101
16分钟前
3
0
docker容器内安装yum,vim等命令,以及配置ls指令

使用国内镜像 mv /etc/apt/sources.list /etc/apt/sources.list.bakecho "deb http://mirrors.163.com/debian/ jessie main non-free contrib" >> /etc/apt/sources.listecho "deb http://......

长恭
16分钟前
3
0
超详细的MySQL8.0.17版本安装教程

下载MySQL MySQL的官网地址:https://www.mysql.com/。 如下图所示: 然后点击DOWNLOADS ==> Community,选择MySQL Community Server。如图所示: 滑到页面的最下面,找到Recommended Downl...

彩色泡泡糖
20分钟前
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部