AOP/Annotation/ScriptEngine 实现复杂鉴权 逻辑,支持自定义逻辑操作和条件表达式的的鉴权

简介:

继昨天发布了使用AOP切面和注解消除重复鉴权的代码和文章,又接到了小伙伴提的新需求。

小伙伴A的需求: 存在多个注解时,目前多个鉴权间是 OR 关系(有一个权限位校验成功则成功),能否支持 AND (都鉴权成功才可以)关系呢 ?

小伙伴B的需求:能否根据方法的参数值进行鉴权呢?例如这个方法 get(String domain ), 我希望domain == "taobao.com" 时进行 XXX权限的鉴权。

这当然难不倒我啦,在XX分钟后我就完成了上述需求。 AND、OR的这个我就不描述怎么搞了,直接看代码即可。 今天想顺便介绍下 java 6后新增的的一个神器 ScriptEngine,善用这个东东,可以简化很多的代码。 那么这个ScriptEngine 是什么呢?下面说下个人通俗的理解

ScriptEngine 是java内置的一个解释器,可以在运行时解释执行你的js代码。有了这个神器,以前以拙劣的代码写的类似解释器的代码可以统统抛弃了。仅仅几行代码就搞定,是不是有一种很爽的感觉?

下面就分享下这个加强版的鉴权实现代码,供大家参考:

@Repeatable(AclRightAnnotations.class)
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AclRightAnnotation {
    AclRightType value() ;
    ResultType result() default ResultType.JSON_EXCEPTION; //action/forward
    LogicType logic() default LogicType.OR; //action/forward
    String expr() default "";
}

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AclRightAnnotations {
    AclRightAnnotation[] value() ;
}

public enum LogicType {
    OR,
    AND
}
public enum ResultType {
    FORWORD,
    JSON_EXCEPTION
}

@Aspect
public class AclRightAspect {
    final Logger logger = LoggerFactory.getLogger(getClass());

    @Pointcut("@within(com.taobao.bright.service.system.auth.AclRightAnnotation) && execution(public * *(..))")
    public void classPointCut() {}

    @Pointcut("@annotation(com.taobao.bright.service.system.auth.AclRightAnnotation) && execution(public * *(..))")
    public void actionPointCut() {}

    @Around("classPointCut() || actionPointCut()")
    public void checkPermission(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature methodSig = (MethodSignature) joinPoint.getSignature();
        AclRightAnnotation[] methodAnnotations = methodSig.getMethod().getDeclaredAnnotationsByType(AclRightAnnotation.class);
        AclRightAnnotation[] classAnnotations = joinPoint.getTarget().getClass().getDeclaredAnnotationsByType(AclRightAnnotation.class);
        List<AclRightAnnotation> annotations = new ArrayList<>();
        annotations.addAll(Arrays.asList(classAnnotations));
        annotations.addAll(Arrays.asList(methodAnnotations));
        boolean hasRight = false;
        String right = "";
        ResultType resultType = ResultType.JSON_EXCEPTION ;
        LogicType logicType = LogicType.OR;
        String expr = "";
        for(AclRightAnnotation acl : annotations){
            resultType = acl.result();
            logicType = acl.logic();
            right= acl.value().getValue();
            expr = acl.expr();
            //根据输入参数的值鉴权
            if(!ValidateUtils.isEmpty(expr)){

               //注入参数变量到ScriptEngine环境
                String[] argNames = methodSig.getParameterNames();
                Object[] argVals = joinPoint.getArgs();
                SimpleBindings simpleBindings = new SimpleBindings();
                for(int i=0; i<argNames.length; i++){
                    simpleBindings.put(argNames[i], argVals[i]);
                }
                try {

                    ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
                    //注解中的表达式执行
                    boolean isProcess = (boolean) engine.eval(expr, simpleBindings);
                    if(!isProcess){
                        continue;
                    }
                } catch (Exception e) {
                    logger.error(e.getMessage(), e.getCause());
                    continue;
                }
            }
            if(logicType == LogicType.OR ) {
                if (AclRightCache.checkCurrentUserRight(right)) {
                    hasRight = true;
                    break;
                } else {
                    hasRight = false;
                }
            }
            if(logicType == LogicType.AND){
                if(AclRightCache.checkCurrentUserRight(right)){
                    hasRight = true ;
                }else {
                    hasRight = false;
                    break;
                }
            }
        }
        if(!hasRight){
            Object[] methodArgs = joinPoint.getArgs();
            for(Object arg: methodArgs){
                if(ResultType.JSON_EXCEPTION == resultType) {
                    if (arg instanceof Context) {
                        Context context = (Context) arg;
                        Result<String> result = new Result<>();
                        result.addActionError("No permission!");
                        context.put(Result.KEY_SIGN, result);
                    }
                }else {
                    if (arg instanceof Navigator) {
                        Navigator nav = (Navigator) arg ;
                        nav.forwardTo("right/noAclRight").withParameter("aclRightType", right);
                    }
                }
            }
        }else {
            joinPoint.proceed();
        }
    }
}

使用样例:


//下面代码的实现的鉴权逻辑为:(  hasRight(AAA) AND  (  domain == 'www.aliyun.com' ? hasRight(BBB) : true ) 。   注意:expr 是js语法的 代码。

@AclRightAnnotation(value = AclRightType.AAA, logic=LogicType.AND )
@AclRightAnnotation(value = AclRightType.BBB, logic=LogicType.AND ,expr="domain == 'www.aliyun.com'")

public class Action {
    public void execute(String domain, HttpServletRequest req, ParameterParser params,Navigator nav) throws Exception {
     
    }
}

//权限注解可以加到类或方法上。 加在类上,会对类中所有public方法都生效。 多个权限位之间可以指定逻辑关系 AND/OR,默认为OR。

//下面代码的实现的鉴权逻辑为:(  hasRight(AAA) OR  (  domain == 'www.aliyun.com' ? hasRight(BBB) : true ) 。  逻辑操作符默认是 OR。

@AclRightAnnotation(value = AclRightType.AAA,   )
@AclRightAnnotation(value = AclRightType.BBB ,expr="domain == 'www.aliyun.com'")

public class Action {
    public void execute(String domain, HttpServletRequest req, ParameterParser params,Navigator nav) throws Exception {
     
    }
}
目录
相关文章
|
9月前
|
缓存 Java Sentinel
Springboot 中使用 Redisson+AOP+自定义注解 实现访问限流与黑名单拦截
Springboot 中使用 Redisson+AOP+自定义注解 实现访问限流与黑名单拦截
|
25天前
|
XML Java 测试技术
Spring AOP—通知类型 和 切入点表达式 万字详解(通俗易懂)
Spring 第五节 AOP——切入点表达式 万字详解!
94 25
|
4月前
|
Java API 数据安全/隐私保护
(工作经验)优雅实现接口权限校验控制:基于自定义注解、AOP与@ConditionalOnProperty配置开关的通用解决方案
(工作经验)优雅实现接口权限校验控制:基于自定义注解、AOP与@ConditionalOnProperty配置开关的通用解决方案
126 1
|
3月前
|
Java 开发者 Spring
Spring AOP深度解析:探秘动态代理与增强逻辑
Spring框架中的AOP(Aspect-Oriented Programming,面向切面编程)功能为开发者提供了一种强大的工具,用以将横切关注点(如日志、事务管理等)与业务逻辑分离。本文将深入探讨Spring AOP的底层原理,包括动态代理机制和增强逻辑的实现。
75 4
|
4月前
|
存储 缓存 Java
Spring高手之路23——AOP触发机制与代理逻辑的执行
本篇文章深入解析了Spring AOP代理的触发机制和执行流程,从源码角度详细讲解了Bean如何被AOP代理,包括代理对象的创建、配置与执行逻辑,帮助读者全面掌握Spring AOP的核心技术。
104 3
Spring高手之路23——AOP触发机制与代理逻辑的执行
|
3月前
|
JSON Java 数据库
SpringBoot项目使用AOP及自定义注解保存操作日志
SpringBoot项目使用AOP及自定义注解保存操作日志
83 1
|
4月前
|
缓存 NoSQL Java
Springboot自定义注解+aop实现redis自动清除缓存功能
通过上述步骤,我们不仅实现了一个高度灵活的缓存管理机制,还保证了代码的整洁与可维护性。自定义注解与AOP的结合,让缓存清除逻辑与业务逻辑分离,便于未来的扩展和修改。这种设计模式非常适合需要频繁更新缓存的应用场景,大大提高了开发效率和系统的响应速度。
129 2
|
8月前
|
监控 Java Spring
自定义注解+AOP切面日志+源码
自定义注解+AOP切面日志+源码
60 1
|
8月前
|
Java Spring
自定义注解+AOP
自定义注解+AOP
81 1
|
7月前
|
容器
springboot-自定义注解拦截ip aop和ioc
springboot-自定义注解拦截ip aop和ioc