springAop实现权限管理数据校验操作日志的场景分析

作者:搞笑大爷 时间:2023-11-23 10:00:27 

前言

作为一个写java的使用最多的轻量级框架莫过于spring,不管是老项目用到的springmvc,还是现在流行的springboot,都离不开spring的一些操作,我在面试的时候问的最多的spring的问题就是我们在平常的项目中使用spring最多的有哪几个点
在我看来无非就两个

  1. spring的bean管理,说的高大上一点就是spring的ioc,di

  2. spring的AOP

spring是一个很强大的轻量级框架,功能远不止这两点,但是我们用的最多的就是这两点。

spring bean 管理

想我们常用的 @Controller @Service @Component 等等都是将我们的bean交给spring管理,我们在获取bean的时候就直接通过 @Resource 就可以获取,当然@Resource 不是spring的,@Autowired 才是spring的,这样我们可以很方便的管理我们的各种bean,使用起来也很方便,不用到处new

springAOP

这个应该是spring面试最常问道的问题了,我面试的时候一般不直接问,我会说一个场景,如果面试者使用过aop立马就可以回答出来。
有这么一个场景,一个系统已经开发完成了,而且已经上线运行了一段时间,很稳定了,现在要加一个功能,就是想收集用户的操作日志,操作日志要比较细致,比如 某某人,在哪个时间点,操作了哪个模块,请求的参数是什么样子的,操作结果如何,等一些比较细致的操作。很多面试者第一时间想到的就是使用过滤器,试想一下,过滤器真的能做到记录这么细致的内容吗?有的可能会想到,我们定义一个公共的方法,所有需要记录日志的地方都去调用这个方法,等等。其实这些都不好,最好的当然是使用aop,使用aop侵入性最小,系统已经稳定运行了,不能去动之前的代码,我们做个aop就可以了,对原来的代码几乎0侵入,对系统影响最小。
那aop实现的方式有哪几种呢?aop实现的步骤又是怎么样的呢?aop还能做些什么呢?这些问题如果在实际项目中使用过,一定能回答出来,如果没有使用过,估计有点难回答,这些都是项目框架的东西,很多公司的项目这一块已经封装好了,很多人直接一直在用,但是没有去查看源码,就不知道具体的实现,实现起来其实也很简单。

接下来我们就以一个小例子来说明下如何使用aop
aop总结起来最常用的就两种方式
1、采用声明的方式来实现(基于XML) 胡子眉毛一把抓,哈哈
2、是采用注解的方式来实现(基于AspectJ)精准定位
我习惯使用注解的方式,更加灵活,使用起来也方便,接下来就以注解的方式来讲下如何使用aop来做权限校验,数据校验,操作日志记录

申明切面

我们使用springaop,首先要将定义的类交给spring管理,然后使用aspectj 定义切面,我们要额外引入
aspectj


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

@Component
@Aspect
public class OperationInterceptor {

}

定义切点

我们采用注解的方式,那么我们首先要申明一个注解


@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface GlobalInterceptor {

/**
  * 权限编码
  */
 PermissionCodeEnum permissionCode();

/**
  * 操作模块
  *
  * @return
  */
 OperModuleEnum opObject() default OperModuleEnum.NO_OBJECT;

/**
  * 操作描述
  *
  * @return
  */
 String opDescription() default "";

}

然后在我们的切面中定义切点


@Component
@Aspect
public class OperationInterceptor {

@Pointcut("@annotation(com.xx.xx.GlobalInterceptor)")
private void opMethods() {
 }

}

定义通知类型

我们要输入还想要输出,那么我们就要将目标方法包围,所以使用around


@Component
@Aspect
public class OperationInterceptor {

@Pointcut("@annotation(com.xx.xx.GlobalInterceptor)")
private void opMethods() {
 }

@Around("opMethods()")
 public Object doMethod(ProceedingJoinPoint point) throws BusinessException {
  //TODO 业务代码
  return null;
}
}

这样一个完整的切面就定义好了

使用切面

在我们的controller中直接使用,我们拿一个登录来讲


@RequestMapping("/login")
@GlobalInterceptor(permissionCode = PermissionCodeEnum.NO_PERMISSION, opObject = OperModuleEnum.OBJECT_LOGIN, opDescription = "登录账号:#{#param1}")
 public AjaxResponseVO login(HttpSession session, @VerifyParam(required = true) String account, @VerifyParam(required = true) String password,@VerifyParam(required = true) String checkCode){

}

我们要传入 切面注解需要的参数
permissionCode 权限编码,我这里定义的是一个枚举,类型自己根据实际情况定义
opObject 操作模块
opDescription 操作描述,这里简单的组织下描述文字,参数的地方使用占位符,到时候根据占位符index获取具体的参数

这样我们在切面中就可以拿到这些参数


@Component
@Aspect
public class OperationInterceptor {

@Pointcut("@annotation(com.xx.xx.GlobalInterceptor)")
private void opMethods() {
 }

@Around("opMethods()")
 public Object doMethod(ProceedingJoinPoint point) throws BusinessException {
  Object obj = null;
   try {
     /**
      * 获取登录信息
      */
     SessionUserDto sessionUserDto = getSessionUser();
     /**
      * 获取目标切点
      */
     Object target = point.getTarget();
     /**
      * 获取参数
      */
     Object[] arguments = point.getArgs();
     /**
      * 获取方法名
      */
     String method = point.getSignature().getName();
     /**
      * 获取参数类型
      */
     Class<?>[] parameterTypes = ((MethodSignature) point.getSignature()).getMethod().getParameterTypes();
     /**
      * 获取具体的方法
      */
     Method m = target.getClass().getMethod(method, parameterTypes);

GlobalInterceptor interceptor = m.getAnnotation(GlobalInterceptor.class);
     if (null == interceptor) {
       return obj;
     }
     /**
      * 校验权限
      */
     if (sessionUserDto != null) {
       validatePermission(interceptor, sessionUserDto);
     }

/**
      * 校验参数
      */
     validateParams(interceptor, m, arguments);

/**
      * 获取描述信息,这里在执行方法之前组织好描述信息,当参数是传值引用时 执行具体方法后,会改变原始参数对象值
      */
     String description = getDescription(arguments, interceptor.opDescription());
     /**
      * 执行操作
      */
     obj = point.proceed();

/**
      * 记录日志
      */
     wirteLog(obj, description, interceptor, sessionUserDto);

} catch (BusinessException e) {
     logger.error("全局 * 异常", e);
     throw e;
   } catch (Exception e) {
     logger.error("全局 * 异常", e);
   } catch (Throwable e) {
     logger.error("全局 * 异常", e);
   }
   return obj;
}
}

这里只贴出了部分代码,完整的代码可以到这里获取

总结

回到最开始的问题,这样我们实现了一个对原有系统侵入极小,然后又实现了操作日志的解决方案。 我们使用spring的aop非常简单,我们使用aop结合反射,可以做很多事情。aop对代码的侵入非常小,不需要动原来的代码,只需要在原有的方法上加一个注解就可以完成对系统的改造,加权限,加日志 等等一系列操作。

来源:https://blog.csdn.net/weixin_45574655/article/details/114776505

标签:spring,Aop,权限管理,数据校验
0
投稿

猜你喜欢

  • Java监听器的作用及用法代码示例

    2023-06-24 06:59:07
  • Unity 静态变量跨场景操作

    2023-10-16 14:32:41
  • Android 7.0以上版本实现应用内语言切换的方法

    2022-12-21 00:25:31
  • Android编程实现canvas绘制饼状统计图功能示例【自动适应条目数量与大小】

    2023-05-10 23:27:41
  • logback使用filter过滤日志操作

    2022-07-11 03:20:27
  • Android RadioGroup多行显示效果 解决单选问题

    2023-05-28 18:23:55
  • 如何在C#项目中链接一个文件夹下的所有文件详解

    2022-09-08 08:48:22
  • android使用gesturedetector手势识别示例分享

    2023-08-08 15:06:11
  • C#泛型用法实例分析

    2021-10-16 14:01:49
  • 基于Mock测试Spring MVC接口过程解析

    2023-11-27 12:04:30
  • Mybatis-Plus设置全局或者局部ID自增的实现

    2021-11-27 00:30:03
  • Java多线程同步工具类CyclicBarrier的使用

    2021-08-26 05:29:53
  • Java基于递归和循环两种方式实现未知维度集合的笛卡尔积算法示例

    2021-07-19 04:41:18
  • 详解Android 全局弹出对话框SYSTEM_ALERT_WINDOW权限

    2023-06-28 04:07:44
  • C#使用Process类调用外部exe程序

    2023-02-12 16:54:20
  • Android实现彩信附件的添加与删除功能

    2023-04-14 01:02:28
  • Android运行时权限终极方案(PermissionX)

    2022-09-10 06:08:02
  • Android Studio实现进度条效果

    2021-10-12 05:37:17
  • VisualStudio Community2019在安装的过程中无法进入安装界面的解决方法

    2023-02-09 11:24:34
  • spring获取bean的源码解析

    2023-10-11 22:15:43
  • asp之家 软件编程 m.aspxhome.com