spingboot aop注解方式实现审计日志功能(完整源码)

您所在的位置:网站首页 debugmax日志 spingboot aop注解方式实现审计日志功能(完整源码)

spingboot aop注解方式实现审计日志功能(完整源码)

2023-04-01 01:07| 来源: 网络整理| 查看: 265

9102年12月25日

设计选型

最近工作上一个新需求,做一个审计日志页面,显示系统各模块的操作(增删改)记录。查阅资料,确定了两种思路: 1.spring aop拦截controller层实现 2.mybatis拦截插件实现

比较了一下两种思路: aop需要给每个接口上添加注解,这样整个系统的接口都要做变动; mybatis插件只需要做一个拦截器,代码改动小。

最后还是确定了使用AOP方式,因为这个需求重业务,不重SQL。如果审计表还需要记录更改前的内容,更改后的内容,变更内容等SQL相关的字段,那么可以使用mybaits拦截插件实现。

表设计

审计表建表语句如下:

因为要求大参数(长度超过1000的参数)不做记录,故参数字段长度设为了1000 复制代码 DROP TABLE IF EXISTS `audit_log`; CREATE TABLE `audit_log` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'ID', `record_type` tinyint(1) DEFAULT NULL COMMENT '记录类型:0-操作记录;1-异常记录', `operation_type` tinyint(1) DEFAULT NULL COMMENT '操作类型:0-新增;1-修改;2-删除', `uid` int(11) DEFAULT NULL COMMENT '操作人ID', `uname` varchar(30) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '操作人', `ip` varchar(255) DEFAULT NULL COMMENT '操作人IP', `desc` varchar(255) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '操作描述', `create_date` datetime DEFAULT NULL COMMENT '操作时间', `method` varchar(255) DEFAULT NULL COMMENT '请求方法名', `params` varchar(1000) DEFAULT NULL COMMENT '请求方法参数', `time` int(11) DEFAULT NULL COMMENT '请求时长', `exception_desc` varchar(1000) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '异常描述', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COMMENT='审计日志表'; 复制代码项目结构图

代码实现 一、配置文件

1.引入对应的maven依赖

org.springframework.boot spring-boot-starter-aop 复制代码

2.修改application.properties配置文件,开启aop

spring.aop.auto=true 复制代码二、创建实体类 @Alias("AuditLogModel") @Data public class AuditLogModel { private DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); private int id; private int recordType; private int operationType; private int uid; private String uname; private String ip; private String desc; private Date createDate; private String method; private String params; private long time; private String exceptionDesc; // 格式化日期 public String getCreateDateStr() { if (createDate != null) { return format.format(createDate); } else { return ""; } } } 复制代码三、定义日志记录元注解 @Documented @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface SystemControllerLog { /** * 描述业务方法 例:Xxx管理-执行Xxx操作 * @return */ // 方法描述 String description() default ""; // 是否需要记录方法参数 boolean recordParams() default true; // 方法类型(0-新增,1-修改,2-删除) int operationType(); } 复制代码

元注解三个属性:

请求方法描述 是否记录请求方法参数(敏感方法:登陆、退出、修改密码以及大参数不作记录) 方法类型(0-新增,1-修改,2-删除) 四、aop切点类

这个是最主要的类,可以使用自定义注解或针对包名实现AOP增强。

这里实现了对自定义注解的环绕增强切点和抛出异常增强切点,对使用了自定义注解的方法进行AOP切面处理; 对方法名,参数名,参数值,日志描述的优化处理; 对方法运行时间进行监控

具体:

使用@Aspect注解在类上声明切面 使用@PointCut注解定义切点,标记方法 使用@Around,@AfterThrowing标明切点时机 @Aspect @Component public class SystemLogAspect { @Autowired(required=false) HttpServletRequest request; @Autowired private AuditLogService auditLogService; private static final int MAX_LENGTH_TO_RECORD_PARAMS = 1000; /** * Controller层切点 注解拦截 */ @Pointcut("@annotation(com.gf.devplat.retention.SystemControllerLog)") public void controllerAspect(){} /** * 环绕增强 * @param joinPoint * @return * @throws Throwable */ @Around("controllerAspect()") public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable{ Object res = null; long time = System.currentTimeMillis(); try { res = joinPoint.proceed(); // 执行时长(毫秒) time = System.currentTimeMillis() - time; return res; } finally { try { //方法执行完成后增加日志 addAuditLog(joinPoint, time); }catch (Exception e){ System.out.println("LogAspect 操作失败:" + e.getMessage()); e.printStackTrace(); } } } /** * 插入操作记录 * @param joinPoint * @param time */ public void addAuditLog(JoinPoint joinPoint, long time) { UserInfo userInfo = SessionUtils.getUserInfo(); AuditLogModel log = new AuditLogModel(); // 操作记录 log.setRecordType(Constants.OperateRecordType.OPERATE_RECORD_NORMAL); // 操作记录,会记录方法请求时间 log.setTime(time); if (userInfo != null) { // Log对象封装值 setLogValue(joinPoint, userInfo, log); // 插入操作记录 auditLogService.addAuditLog(log); } } /** * 异常通知,插入异常记录 * @param joinPoint * @param e */ @AfterThrowing(pointcut = "controllerAspect()", throwing = "e") public void doAfterThrowing(JoinPoint joinPoint, Throwable e){ UserInfo userInfo = SessionUtils.getUserInfo(); AuditLogModel log = new AuditLogModel(); // 1,表示异常记录 log.setRecordType(Constants.OperateRecordType.OPERATE_RECORD_EXCEPTION); log.setExceptionDesc(e.toString()); if (userInfo != null) { // Log对象封装值 setLogValue(joinPoint, userInfo, log); // 插入异常记录 auditLogService.addAuditLog(log); } } /** * Log对象封装值 * @param joinPoint * @param userInfo * @param log */ public void setLogValue(JoinPoint joinPoint, UserInfo userInfo, AuditLogModel log){ // 是否记录参数 boolean recordParams = true; log.setUname(userInfo.getName()); log.setUid(userInfo.getUid()); log.setCreateDate(new Date()); String ip = SessionUtils.getIpAddress(request); log.setIp(ip); MethodSignature signature = (MethodSignature) joinPoint.getSignature(); // 请求方法 Method method = signature.getMethod(); SystemControllerLog controllerLog = method.getAnnotation(SystemControllerLog.class); if (controllerLog != null) { // 请求方法上的注解 String description = controllerLog.description(); recordParams = controllerLog.recordParams(); log.setDesc(description); log.setOperationType(controllerLog.operationType()); } // 请求方法名 String className = joinPoint.getTarget().getClass().getName(); String methodName = signature.getName(); log.setMethod(className + "." + methodName); // 请求方法参数值 Object[] args = joinPoint.getArgs(); // 请求方法参数名称 LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer(); String[] paramNames = u.getParameterNames(method); if (args != null && paramNames != null && recordParams) { String params = ""; for (int i = 0; i < args.length; i++) { params += " " + paramNames[i] + ": " + args[i]; } // 长度超过1000字符串的大参数也不记录 if (params.length()


【本文地址】


今日新闻


推荐新闻


    CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3