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

时间: 2024-11-08 12:58:41

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

Shiro权限验证代码记录,正确找到shiro框架在什么地方做了权限识别

权限验证方式的验证代码: org.apache.shiro.web.servlet.AdviceFilter这个类是所有shiro框架提供的默认权限验证实例类的父类 验证代码: public void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException { Exception exception = nu

Asp.net Mvc 身份验证、异常处理、权限验证(拦截器)实现代码

本问主要介绍asp.net的身份验证机制及asp.net MVC拦截器在项目中的运用.现在让我们来模拟一个简单的流程:用户登录>权限验证>异常处理 1.用户登录 验证用户是否登录成功步骤直接忽略,用户登录成功后怎么保存当前用户登录信息(session,cookie),本文介绍的是身份验证(其实就是基于cookie)的,下面看看代码. 引入命名空间 using System.Web.Security; Users ModelUser = new Users() { ID = 10000, Nam

简单两步快速实现shiro的配置和使用,包含登录验证、角色验证、权限验证以及shiro登录注销流程(基于spring的方式,使用maven构建)

前言: shiro因为其简单.可靠.实现方便而成为现在最常用的安全框架,那么这篇文章除了会用简洁明了的方式讲一下基于spring的shiro详细配置和登录注销功能使用之外,也会根据惯例在文章最后总结一下shiro的大致配置使用流程,希望本篇文章能够后能给大家一种原来shiro这么简单的错觉感觉. 注意:该篇文章的开始是建立在一个完备的spring+mybatis的开发环境中,除了shiro之外的配置基本不会涉及到.做好自己--eguid原创文章 一.依赖的jar包 本篇文章使用shiro-1.4

Spring MVC + Shiro 实现权限验证

MAVEN的pom.xml 引入shiro(Spring MVC+mybatis 请参见上一章). <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.2.5</version> </dependency> <!-- http://mvnrepository.co

项目一:第十三天 1、菜单数据管理 2、权限数据管理 3、角色数据管理 4、用户数据管理 5、在realm中动态查询用户权限,角色 6、Shiro中整合ehcache缓存权限数据

1 课程计划 菜单数据管理 权限数据管理 角色数据管理 用户数据管理 在realm中动态查询用户权限,角色 Shiro中整合ehcache缓存权限数据         2 菜单数据添加 2.1 使用combotree父菜单项数据     1. 页面:menu_add.jsp 2. 修改组件样式:easyui-combotree,修改url  树型表格treeGrid跟下来数combotree要求数据格式基本一致. Combotree通过text属性展示文本.   3. 使用treegrid组件的

Spring boot 入门(四):集成 Shiro 实现登陆认证和权限管理

本文是接着上篇博客写的:Spring boot 入门(三):SpringBoot 集成结合 AdminLTE(Freemarker),利用 generate 自动生成代码,利用 DataTable 和 PageHelper 进行分页显示.按照前面的博客,已经可以搭建一个简单的 Spring Boot 系统,本篇博客继续对此系统进行改造,主要集成了 Shiro 权限认证框架,关于 Shiro 部分,在本人之前的博客(认证与Shiro安全框架)有介绍到,这里就不做累赘的介绍. 此系列的博客为实践部分

简单扩展让beetl HTML标签支持父子嵌套

默认情况下,Beetl的html标签并不支持父子嵌套,就像类似jsp标签那样,父标签需要知道子标签的信息,子标签也需要知道父标签信息.但是beetl只需要简单扩展,就能完成嵌套标签支持. 首先看一个最终的使用效果,实现俩个html标签table.tag,tr.tag.可以在页面上这么用: <#table data ="${userList}"> <#tr class="3c" name="name"> 名称 </#t

SpringMVC+Apache Shiro+JPA(hibernate)案例教学(二)基于SpringMVC+Shiro的用户登录权限验证

序: 在上一篇中,咱们已经对于项目已经做了基本的配置,这一篇文章开始学习Shiro如何对登录进行验证. 教学: 一.Shiro配置的简要说明. 有心人可能注意到了,在上一章的applicationContext.xml配置文件中,包含以下配置. <!-- 項目自定义的Realm --> <bean id="shiroDbRealm" class="org.shiro.demo.service.realm.ShiroDbRealm" ><

递归扩展变量和简单扩展变量

1 递归扩展变量和简单扩展变量的区别 最大的区别在于,递归扩展变量等号右边的表达式里面的变量要等到该递归变量被用的时候再去扩展,而简单变量的话,在读取makefile的时候就已经扩展了. 并且简单扩展变量有前后关系,简单扩展变量只能用到在其前面定义的变量,而不能用到在其后面定义的变量.而递归扩展变量,要用的时候,需要扩展的时候,可以一直扩展到不能扩展为止,即递归扩展. 2 例子 foo := $(bar) bar = aaa all:;echo $(foo) 输出为空,因为在给foo赋值扩展等号