终于可以向重复的鉴权代码说byebye 了 -- (玩转 AOP和Annotation )

简介:

缘起

最近在写代码时,不可避免的会写成出进行大量的鉴权逻辑。 如下面代码所示,每一个方法都要添加一个鉴权逻辑(样例只有4个方法,实际工作中会很多)

class Action{

    public void get() {
        checkRights(); //鉴权代码
        //业务代码
    }

    public void put() {
        checkRights(); //鉴权代码
        //业务代码
    }

    public void delete() {
        checkRights(); //鉴权代码
        //业务代码
    }

    public void post() {
        checkRights(); //鉴权代码
        //业务代码
    }
}

作为一个善于偷懒的码农,看到如上代码应该会敏锐得意识到‘Don't repeat yourself’的编程箴言了,重复代码带来的问题,我就不再累述了,相信大家都身受其害。

如何解决呢?

那么该如何消除到这些大量烦人的重复逻辑呢。 有的同学可能已经想到了,可以在web框架的流水线中增加一个鉴权处理器,这的确是一个可行方案,我们这里就不详细解释了。 这里给出另外一个利用AOP进行重复代码消除的技术,那么什么是AOP呢?

这里说下个人比较粗浅的理解:AOP(Aspect-Oriented Programming)面向切面编程,是一种根据位置描述规则(Pointcut切点)把代码(Advice)添加(织入weav)到指定位置的技术。 通过 (位置描述规则 + 代码)把散落在代码中的重复逻辑提取出来,对重复代码完成的这形式的抽象就叫AOP。

AOP在天然支持元编程的语言里面(如lisp),很容易直接写代码实现。 但是在大部分不支持元编程的语言,就由各种工具或框架各显神通了,如C++里面有AspectC++等。 在java世界中有2个著名的实现一个是经典全能版的AspectJ、另外一个阉割版的Spring AOP。 我们的项目使用spring,一开始很自然的想使用 SpringAOP,但是很不幸 SpringAOP 仅支持容器管理对象的AOP,这无法满足我的需求。 所以只能使用AspectJ了。 AspectJ 有2种织入方式(使用acj编译器静态织入,使用javaagent动态织入),这里顺便提一下 javaagent技术,它允许用户在类加载前改变类的字节码,受益于java的动态能力,aspectJ可以优雅的实现动态织入技术。 AOP的核心思想是比较简单的,但是切点规则相当繁琐,不容易全面掌握,Pointcut表达式以后再详细写吧。 把AOP run起来,废了不少时间,解决了各种奇葩问题,因此现在这里写一下过程,供对AOP应用有兴趣的同学参考。

项目实践

下面以项目为例详细说明实现过程,这里使用了动态织入的方式:

增加包依赖

注意: aspectJ包的版本和java 版本一致,否则运行时会出现类被损坏的异常。 我们项目使用的java8,这个地方我掉过坑。

<dependency>
         <groupId>org.aspectj</groupId>
          <artifactId>aspectjweaver</artifactId>
          <version>1.8.12</version>
  </dependency>


<dependency>
         <groupId>org.aspectj</groupId>
         <artifactId>aspectjrt</artifactId>
         <version>1.8.12</version>
  </dependency>

classpath路径里面增加 META-INFO/aop.xml

配置很简单,一个切面类。 织入目标的包路径(注意要包含切面类的路径,否则切面类会缺少需要被织入的aspectOf()方法,这也是掉过坑的)

<?xml version="1.0"?>
<aspectj>
    <aspects>
        <aspect name="com.taobao.bright.service.system.auth.AclRightAspect"/>
    </aspects>
    <weaver options="-showWeaveInfo ">
        <include within="com.taobao.bright.web.sre.module..*" />
        <include within="com.taobao.bright.service.system.auth.*" />
    </weaver>
</aspectj>

启动方式

有2种方式

1.  javaagent方式  (我使用的这种方式)
java "-javaagent:/Users/qiyu/.m2/repository/org/aspectj/aspectjweaver/1.8.7/aspectjweaver-1.8.7.jar"

2. tomcat server.xml配置文件增加
<Context><Loader loaderClass="org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader"/> </Context> 

实现代码样例

废话少说,放码过来!

2个注解类,一个切面类都贴出来了,供大家参考借鉴。


//权限位注解
@Repeatable(AclRightAnnotations.class)
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AclRightAnnotation {
    AclRightType value() ;
}
//可以添加多个权限位的注解 (java8新特性)
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AclRightAnnotations {
    AclRightAnnotation[] value() ;
}


@Aspect
public class AclRightAspect {

   //含有AclRightAnnotation注解的目标类且为public方法 切点定义
    @Pointcut("@within(com.taobao.bright.service.system.auth.AclRightAnnotation) && execution(public * *(..))")
    public void classPointCut() {}

   //含有AclRightAnnotation注解的方法且为public方法 切点定义
    @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();

     //通过getDeclaredAnnotationsByType反射方法,获取注解中的权限位
        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 = "";
        for(AclRightAnnotation acl : annotations){
                right= acl.value().getValue();
                if(AclRightCache.checkCurrentUserRight(right)){
                    hasRight = true ;
                    break;
                }
        }
        if(!hasRight){
            Object[] methodArgs = joinPoint.getArgs();
            for(Object arg: methodArgs){
                    if (arg instanceof Context) {  //返回鉴权异常给客户端
                        Context context = (Context) arg;
                        Result<String> result = new Result<>();
                        result.addActionError("No permission!");
                        context.put(Result.KEY_SIGN, result);
                    }
            }
        }else {
            joinPoint.proceed();
        }
    }
}

使用代码样例

使用起来就很简单了,直接在类或方法上增加注解就可以了。 一行代码就可以搞定这个类的注释了。如果方法上需要特殊的权限,也可以增加注解。

//权限注解可以加到类或方法上。 加在类上,会对类中所有public方法都生效。 多个权限位之间是OR的关系,即有一个权限位验证通过即可。


@AclRightAnnotation(value = AclRightType.BRIGHT_STATION_VIP_MANAGE)
class Action{

    public void get() {
        //业务代码
    }

    public void put() {
        //业务代码
    }

    public void delete() {
        //业务代码
    }
    public void post() {
        //业务代码
    }
}

AOP的使用过程大致如上,使用如上方法后消除了项目代码中大量的重复鉴权代码。欢迎各位参考交流。

目录
相关文章
|
3月前
|
XML Java 数据格式
Spring-AOP综合代码演示讲解
Spring-AOP综合代码演示讲解
58 0
|
4天前
|
XML Java 数据格式
Spring5入门到实战------11、使用XML方式实现AOP切面编程。具体代码+讲解
这篇文章是Spring5框架的AOP切面编程教程,通过XML配置方式,详细讲解了如何创建被增强类和增强类,如何在Spring配置文件中定义切入点和切面,以及如何将增强逻辑应用到具体方法上。文章通过具体的代码示例和测试结果,展示了使用XML配置实现AOP的过程,并强调了虽然注解开发更为便捷,但掌握XML配置也是非常重要的。
Spring5入门到实战------11、使用XML方式实现AOP切面编程。具体代码+讲解
|
3月前
|
缓存 人工智能 监控
AOP:让你的代码像超级英雄一样飞翔
AOP:让你的代码像超级英雄一样飞翔
|
3月前
|
Java 开发者 Spring
面向切面编程(SpringAOP)、通过注解实现AOP代码、AOP的工作流程
面向切面编程(SpringAOP)、通过注解实现AOP代码、AOP的工作流程
45 1
面向切面编程(SpringAOP)、通过注解实现AOP代码、AOP的工作流程
|
3月前
|
Java Spring 容器
Spring AOP 代码案例
Spring AOP 代码案例
51 1
|
10月前
|
缓存 监控 安全
Spring AOP 详细深入讲解+代码示例
Spring AOP(Aspect-Oriented Programming)是Spring框架提供的一种面向切面编程的技术。它通过将横切关注点(例如日志记录、事务管理、安全性检查等)从主业务逻辑代码中分离出来,以模块化的方式实现对这些关注点的管理和重用。 在Spring AOP中,切面(Aspect)是一个模块化的关注点,它可以跨越多个对象,例如日志记录、事务管理等。切面通过定义切点(Pointcut)和增强(Advice)来介入目标对象的方法执行过程。 切点是一个表达式,用于匹配目标对象的一组方法,在这些方法执行时切面会被触发。增强则定义了切面在目标对象方法执行前、执行后或抛出异常时所
6635 1
|
3月前
|
Java Spring
代码优雅的转变:基于注解的AOP编程在Spring中的实践
代码优雅的转变:基于注解的AOP编程在Spring中的实践
29 0
|
3月前
|
Java Maven 数据安全/隐私保护
代码优雅升级,提升开发效率:挖掘Spring AOP配置的学习宝藏!
代码优雅升级,提升开发效率:挖掘Spring AOP配置的学习宝藏!
|
12月前
|
监控 前端开发 Java
30个类手写Spring核心原理之AOP代码织入(5)
前面我们已经完成了Spring IoC、DI、MVC三大核心模块的功能,并保证了功能可用。接下来要完成Spring的另一个核心模块—AOP,这也是最难的部分。
46 0
|
设计模式 Java Spring
【Spring】核心部分之AOP:通过列举代码例子,从底层刨析,深入源码,轻轻松松理解Spring的核心AOP,AOP有这一篇足以
【Spring】核心部分之AOP:通过列举代码例子,从底层刨析,深入源码,轻轻松松理解Spring的核心AOP,AOP有这一篇足以