当前位置 博文首页 > 努力充实,远方可期:【aop】10分钟利用aop写注解权限认证
之前学过很多遍AOP切面,但印象中自己只会在切点前后打印写日志,没有实战能力,为此,本篇笔记就点除了“打印日志”之外的AOP常用功能
看这篇笔记的同学应该大都了解过AOP,这里不详述了,只带着大家回忆一下核心知识点。
用途:如前面所言,你可以简单理解为你想在spring容器中某一个方法每次调用前后打印一些日志。(设为计算器的方法cal.add(a,b)
)
add(a,b){打印日志;正常业务;打印日志}
之前你可能学过几遍了,还是记不住,那这里还是简单说下吧。
面>点,所以切面是一个类,而切点是一个正则表达式,他说明要匹配的方法(这个方法叫目标)。
那么日志怎么表示呢?就是使用@Before、@After或者@Around等注解,它代表要在前面正则表达式匹配的方法前后执行一些日志
更多:还有其他的方法以及他们的匹配顺序自己查吧。
前面提及了一个正则表达式的东西,他说明了日志要在哪个方法前后打印。既然是正常表达式了,那么它就可以匹配多个方法打印同样的日志。这个正则表达式的格式为:
返回类型 包名.方法(参数)
这样就能表示任意一个方法了。有几个小点记一下
*
和正则表达式一样,代表任意..*
打包多级*
,比如a.b.c.d(),可以写为a..d()
(..)
表示方法参数任意前面说的日志术语叫做:通知。通知一般都写到单独的一个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个属性,
execution(*org.a.service.impl.*.*(..))")
里面写的就是正则表达式@AfterReturning(pointcut="controller()")
,是说正则表达式不在这里写,而是在切面类另外一个方法controller()上用@PointCut
标注的(至此,我们了解了切点)比如现在有个需求:自己写完权限了,如何让他控制某个方法不被任意调用呢?用切面可以轻易解决
先定义你的一个注解,注解的写法不详述了,都是套路
@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类或接口)")
//实现了该接口的类、继承该类、该类本身的类—的所有方法(包括不是接口定义的方法,包含父类的方法)。必须是在目标对象上声明注解,在接口上声明不起作用该睡觉了。。。
cs