【aop】10分钟利用aop写注解权限认证

2021/6/21 6:28:59

本文主要是介绍【aop】10分钟利用aop写注解权限认证,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

前言

之前学过很多遍AOP切面,但印象中自己只会在切点前后打印写日志,没有实战能力,为此,本篇笔记就点除了“打印日志”之外的AOP常用功能

AOP基础

这这篇笔记的同学应该大都了解过AOP,这里不详述了,只带着大家回忆一下核心知识点。

AOP用途

用途:如前面所言,你可以简单理解为你想在spring容器中某一个方法每次调用前后打印一些日志。(设为计算器的方法cal.add(a,b)

静态代理与动态代理:
  • 静态代理:你自己在那个方法体力自己写日志打印逻辑,add(a,b){打印日志;正常业务;打印日志}
  • 动态代理:不在add()方法体里写,而是在别的java文件里写,这样就达到了解耦或者不侵入代码等目的。其实最终的效果是一样的,spring会帮你在target目录下(或者你看不到的内存中)生成新的class文件,这个class里文件就像你自己写的静态代理一样,写到了方法体了。
核心概念

之前你可能学过几遍了,还是记不住,那这里还是简单说下吧。

面>点,所以切面是一个类,而切点是一个正则表达式,他说明要匹配的方法(这个方法叫目标)。

  • 切面:Aspect
  • 切点:PointCut

那么日志怎么表示呢?就是使用@Before、@After或者@Around等注解,它代表要在前面正则表达式匹配的方法前后执行一些日志

更多:还有其他的方法以及他们的匹配顺序自己查吧。

前面提及了一个正则表达式的东西,他说明了日志要在哪个方法前后打印。既然是正常表达式了,那么它就可以匹配多个方法打印同样的日志。这个正则表达式的格式为:

返回类型  包名.方法(参数)

这样就能表示任意一个方法了。有几个小点记一下

  • *和正则表达式一样,代表任意
  • ..*打包多级*,比如a.b.c.d(),可以写为a..d()
  • (..)表示方法参数任意

hello-world

前面说的日志术语叫做:通知。通知一般都写到单独的一个java类中,并且在对应通知方法上指定了是哪个目标方法 的通知(如计算器的add()方法)

下面的方法是我从其他笔记摘过来的,简单看下即可,他是说:log()方法打印日志,而@AfterReturning是说他在目标方法正常返回后被调用(就是说add方法没有报错执行完了)。

@Aspect
public class LogAspect{
    // 匹配org.a.service.impl包下所有类的、
    // 所有方法的执行作为切入点
    @AfterReturning(returning="rvt", pointcut="execution(*org.a.service.impl.*.*(..))")
    // 声明rvt时指定的类型会限制目标方法必须返回指定类型的值或没有返回值
    // 此处将rvt的类型声明为Object,意味着对目标方法的返回值不加限制
    public void log(Object rvt){
        System.out.println("获取目标方法返回值:" + rvt);
        System.out.println("模拟记录日志功能...");
    }
}

这里还得提下细节:@AfterReturning注解里指定了2个属性,

  • returning是说计算器的add方法返回了一个参数rvt,然后把这个参数传入log(rvt)方法里执行日志通知。
  • pointcut是说该日志通知匹配的目标方法(你理解为正则表达式就可以了)
    • execution(*org.a.service.impl.*.*(..))")里面写的就是正则表达式
    • 但其他通常还有另外一种用法,如@AfterReturning(pointcut="controller()"),是说正则表达式不在这里写,而是在切面类另外一个方法controller()上用@PointCut标注的(至此,我们了解了切点)
    • pointcut和value属性是不同名属性(就是说他俩用哪个都行)

AOP用于权限控制

比如现在有个需求:自己写完权限了,如何让他控制某个方法不被任意调用呢?用切面可以轻易解决

先定义你的一个注解,注解的写法不详述了,都是套路

@Target(ElementType.TYPE) // 限定注解只能标注到类上
@Retention(RetentionPolicy.RUNTIME) // 注解什么时候存在 // RUNTIME就是编译、class、运行class时都存在
@interface MyAnnotation{
    //注解的参数:参数类型+参数名();
    String value() default "";//如果不写默认值,注解上就必须给定参数值
}
@Aspect // 切面
@Component
public class AuthAspect {
    // 自动注入你自己写的权限认证service
    @Autowired
    权限认证Service authService;
    
   // 定义一个切点,用于写正则表达式
    @Pointcut("* com.a.ctrl.*(..))")// ctrl包下的任意方法,任意参数,任意返回值,满足则匹配到
    public void myPointCut1() {}

    // 并且在controller方法上加了你自己写的注解@MyAnnotation的 
    @Pointcut("@annotation(MyAnnotation)")
    public void authMethod(String value){ // 自动转入你注解带来的value信息
        
    }
    
    // 验证
    public void log(ProceedingJoinPoint joinPoint){
        // 获取注解
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        MyAnnotation myAnno = method.getAnnotation(MyAnnotation.class);
        // 比如注解里带来了要验证的内容,是角色还是权限等
        String val = myAnno.value;
        
        // 获取当前用户
        用户没有必要作为参数传过来,可以使用threadlocal等方式放到threadloca中;
        得到的User user;
        // 也可以使用spring的方法进行获取请求信息,然后获取请求里代理的cookie和用户等信息
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
         String info = request.getParameter("name");
        
        // 进行验证myAnno
        authService.验证(user.userId,myAnno.value);
        // 如果验证不通过可以直接抛异常让mvc进行异常捕获或者其他处理方式
        
        // 业务方法
        returnVal = joinPoint.proceed();
    }
    

优化:上面获取注解信息的逻辑应该可以优化,在kotlin里看过相关的逻辑,但是java里不知道有没有类似的,doc里查查应该能查到

就这样,虽然有点虎头蛇尾,但想说的都说完了。

随意再谈点aop其他内容吧

aop进行定义切点时,还有几个其他的属性

  • @Around("@annotation(Transactional)"):标注了@Transactional注解拦截的
  • @Around("within(com.test..*)")包下所有的方法。该类的所有方法都执行aop方法。即要把注解加在类上. 在接口上声明不起作用 。 子孙类经测试匹配不到
  • @Around("this(com.TestService)")实现了该接口的类、继承该类、该类本身的类—的所有方法(包括不是接口定义的方法,但不包含父类的方法)
  • @Around("target(java类或接口)")//实现了该接口的类、继承该类、该类本身的类—的所有方法(包括不是接口定义的方法,包含父类的方法)。必须是在目标对象上声明注解,在接口上声明不起作用

该睡觉了。。。



这篇关于【aop】10分钟利用aop写注解权限认证的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程