写在前面:
之前已经写过一篇
Spring之IOC(DI)附面试题准备
幸运的金荷,公众号:我这一路Spring之IOC(DI)附面试题准备
主要讲的是Spring另一块核心IOC一些相关知识。今天就来聊聊Spring另一块核心AOP。主要也还是最近项目中用到了AOP。说实话之前对AOP也是一知半解,没有很深刻的印象。越学到后面和实际开发中会发现以往学的知识,真正在实际中用到了且经常用到的可能也就占了20-30%了。剩余的可能就是为了那20-30%打基础做铺垫。另外,还想说在实际项目开发实际业务中,把用到了的技术点在去结合其基本知识概念,理解起来可能更为深刻,也更容易理解。拿今天要举的例子AOP来说,之前学习Spring的时候笔记做的满满的,当初学起来感觉很容易,一听就懂了。可在实际项目中要用到了,又无从下手。第一可能是没经验没做过还是首次。完整的知识基本概念没有在实际中应用过。相当于新兵没有实枪实弹上过战场。缺乏了经验。第二不知道该什么时候用。用在哪里。怎么用。这点可能也是困扰初学者的拦路虎。接下来就结合项目中应用AOP为出发点,从头至尾聊聊AOP。
实际问题:
前端向后端来调用接口。一般都是访问@Controller层的接口。使用AOP的话不仅能得到访问接口的url、入参、方法名还能知道何时发起的请求、接口有无调用完成等等一些基本信息。例如,下面这个是我用@Around环绕通知,定义了接口的访问情况。有开始有结束有入参有url等一些基本信息,后期如果有问题,也方便开发人员快速找到问题的根源。
在最近做的项目中,有微信端和PC后端。PC端相当于一个后台管理系统,微信端用于业务的上报等基础操作,PC后端能管理微信端的增删改查。而且账户也各自区分只能登录相对应的各个客户端。不能跨端登陆。是的,相信很多人想到了,用户表里设立一个字段来区分微信端还是PC端。我们这里处理的时候根据用户角色role_type来判断。通过role_type能满足我们现有的业务进行区分。但我们测试人员在测试过程中,把微信端登陆成功之后的token拿来,再使用第三方工具例如Postman去模拟只有PC端身份才能操作的接口,例如只有PC端能看到所有微信端用户信息列表,这时如果把这个接口的url放到Postman上,参数什么的通过在网页上F12获取参数信息,加上微信端用户的token也能访问到。产生安全漏洞。当然,也有人会说,微信端人员怎么能登陆PC端进而获取那些接口url。实际中,可能没有人会注意到,但要是遇到高水平的同行可就说不准了。以防万一对客户负责,顺便锻炼自己的能力。决定采用AOP来对访问接口时进行安全检查。只有PC端的用户才能访问接口。
发现居然调用成功!产生安全漏洞。
在PC端接口上加注解。
再次登陆。发现身份不匹配。
当然处理这个情况的办法有很多,这里主要讲的是应用AOP来解决token安全问题。
解决办法:
自定义一个只有PC端能访问的注解。用来区分微信端和PC端的用户发送的请求。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PermissionValid {
//自定义角色值,默认微信端用户
int role() default 2;
}
去给只有PC端能调用的接口加注解。
建立AOP切面。加上切入点。切入带有@PermissionValid注解的接口。
@Aspect
@Component
public class PermissionValidAspect {
//切入点表达式决定了用注解方式的方法切还是针对某个路径下的所有类和方法进行切,方法必须是返回void类型
@Pointcut("@annotation(com.yscredit.annotation.PermissionValid)")
private void permissionCheckCut() {
}
}
建立一个环绕通知,在调用接口前后进行判断。
//定义了切面的处理逻辑。即方法上加了@PermissionValid
@Around("permissionCheckCut()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
log.info("==========进入AOP==========");
//1.记录日志信息
Signature signature = pjp.getSignature();
String className = pjp.getTarget().getClass().getSimpleName();
String methodName = signature.getName();
log.info("className:{},methodName:{}", className, methodName);
//2.角色权限校验
MethodSignature methodSignature = (MethodSignature) signature;
Method targetMethod = methodSignature.getMethod();
Method realMethod = pjp.getTarget().getClass().getDeclaredMethod(targetMethod.getName(), targetMethod.getParameterTypes());
if (realMethod.isAnnotationPresent(PermissionValid.class)) {
//获取方法上注解中表明的权限
PermissionValid permission = realMethod.getAnnotation(PermissionValid.class);
Integer roleType = UserUtil.getLoginUser().getRoleType();
log.info("当前请求接口的用户角色:{}", roleType);
Integer role = permission.role();
log.info("当前接口需要的用户角色:{}", role);
if (Objects.equals(role, roleType)) {
log.info("AOP权限角色校验通过!");
//3.执行业务逻辑,放行
return pjp.proceed();
} else {
log.info("身份不匹配!");
return WebResponse.resException("无权限访问", null);
}
}
return WebResponse.resException("无权限访问", null);
}
可见AOP在环绕通知中,已经为我们请求接口进行了校验。若有权限,放行。
前面算是抛砖引玉吧对AOP有个大概了解。接下来就学习下AOP一些基本知识概念。
Spring AOP
【基于AspectJ注解配置AOP】
加入jar包
添加注解
在类中声明各种通知
(前置通知@Before)
(后置通知@After)
(返回通知@AfterReturnning)
returning="result"
Object result
syso(......+ result)
(异常通知@AfterThrowing)
(环绕通知@Around)
切入点表达式
切面的优先级
在同一个连接点PointCut上应用不止一个切面时, 除非明确指定, 否则它们的优先级是不确定的。
重用切入点定义
在编写 AspectJ 切面时, 可以直接在通知注解中书写切入点表达式. 但同一个切点表达式可能会在多个通知中重复出现.。
在 AspectJ 切面中, 可以通过@Pointcut注解将一个切入点声明成简单的方法. 切入点的方法体通常是空的。
【基于XML配置AOP】