• 审计日志功能实现优化及测试记录(参照若依系统,以dolphinscheduler 2.0.5 为例,实现相关功能)


    前段时间调研审计日志,发现海豚调度3.0源码已经有这个功能了,测试了一下发现功能并没有完全实现,详见dolphinscheduler 3.0.1 审计日志,至于2.0的版本很早就有审计日志相关类了,只不过都是开发未完成状态。目前是参照若依管理系统的操作日志功能,把代码提取出来之后,稍作优化,本文主要记录做了哪些优化以、使用过程遇到的注意事项及相关技术点。

    🐬使用


    🐠若依-操作日志


    在这里插入图片描述
    在这里插入图片描述

    🐠引入海豚调度


    直接提取若依中记录操作日志的相关类,运用的AOP,切面、切点。核心类为LogAspectLog,提取出来之后,根据报错,缺少哪个类,把相关类拷贝过来即可,我这边没有直接放到海豚调度,而是单独搞了个项目,主要为了方便其他项目也能调用。

    • LogAspect:切面处理类,解析请求响应参数入库等

      com.ruoyi.framework.aspectj.LogAspect
      
      • 1
    • 切点注解接口,拎出来之后,接口名称我改成了LogAnnotation,海豚2.0自带接口为AccessLogAnnotation,只不过是个开发未完成的功能,由于这部分要做成公共的功能,所以直接放弃了海豚这部分的半成品

      com.ruoyi.common.annotation.Log
      
      • 1
    • 表为操作日志记录表-sys_oper_log

      CREATE TABLE `sys_oper_log` (
        `oper_id` bigint NOT NULL AUTO_INCREMENT COMMENT '日志主键',
        `title` varchar(50) DEFAULT '' COMMENT '模块标题',
        `business_type` int DEFAULT '0' COMMENT '业务类型(0其它 1新增 2修改 3删除)',
        `method` varchar(100) DEFAULT '' COMMENT '方法名称',
        `request_method` varchar(10) DEFAULT '' COMMENT '请求方式',
        `operator_type` int DEFAULT '0' COMMENT '操作类别(0其它 1后台用户 2手机端用户)',
        `oper_name` varchar(50) DEFAULT '' COMMENT '操作人员',
        `dept_name` varchar(50) DEFAULT '' COMMENT '部门名称',
        `oper_url` varchar(255) DEFAULT '' COMMENT '请求URL',
        `oper_ip` varchar(128) DEFAULT '' COMMENT '主机地址',
        `oper_location` varchar(255) DEFAULT '' COMMENT '操作地点',
        `oper_param` varchar(2000) DEFAULT '' COMMENT '请求参数',
        `json_result` varchar(2000) DEFAULT '' COMMENT '返回参数',
        `status` int DEFAULT '0' COMMENT '操作状态(0正常 1异常)',
        `error_msg` varchar(2000) DEFAULT '' COMMENT '错误消息',
        `oper_time` datetime DEFAULT NULL COMMENT '操作时间',
        PRIMARY KEY (`oper_id`)
      ) ENGINE=InnoDB AUTO_INCREMENT=1068 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='操作日志记录';
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19

    🐟引入审计日志包,增加LogAnnotation注解


    以工作流定义-修改为例,增加注解,原先的AccessLogAnnotation注解直接去掉了,没用,此时编辑工作流的时候,请求响应参数及方法等都会存到数据库
    在这里插入图片描述

    🐬问题记录及优化


    🐠service方法注解时而生效,时而不生效


    背景:工作流定义-更新,内部涉及三张表的操作,对应多个方法,因此想要记录所有的方法的请求响应参数

    🐟不生效原因


    • 添加注解的方法必须是public,内部私有方法代理是获取不到的
    • 添加注解的方法类必须实例化过
      • 比如工作流定义更新方法,私有方法updateDagDefine,即便改为public,添加注解也不会生效
        在这里插入图片描述

    🐟修改


    🐡自我注入(纯测试)

    改为public,注入本身,再次调用,注解有效(纯测试,虽然spring支持循环依赖,可以自我注入,但是总感觉这样干有风险)
    在这里插入图片描述
    在这里插入图片描述

    🐡接口中增加该方法

    比如改成updateProcessDefinition这种,通过controller中注入该方法接口
    在这里插入图片描述
    在这里插入图片描述

    🐠优化,增加批次号


    背景:工作流定义-更新,内部涉及三张表的操作,对应多个方法,添加注解后全部记录了下来,但是不好区分,哪些记录是同一个操作产生的,即同一个批次,因此增加批次号-batch_no字段,更新工作流操作,底层涉及三种表的更新,这些操作都是在同一个线程,因此可以通过ThreadLocal去实现
    在这里插入图片描述

    🐟ThreadLocal的使用


    ThreadLocal使用很简单,一个set方法,一个get方法,一个remove方法,主要风险在于内存泄漏,一般调用完get方法需要调用remove,也可以实现AutoCloseable ,自动回收,主要何时调用remove不好辨别

    • 新增批次号工具类BatchNoUtils ,实现了AutoCloseable 接口,否则还要单独调用remove方法
      public class BatchNoUtils  implements AutoCloseable {
      
          private static final ThreadLocal<Long> threadLocal = new ThreadLocal<>();
      
          public static void add(Long batchNo) {
              threadLocal.set(batchNo);
          }
      
          public static Long getBatchNo() {
              return threadLocal.get();
          }
      
          @Override
          public void close() {
              threadLocal.remove();
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
    • 增加过滤器BatchNoFilter ,在调用方法之前设置批次号(即threadLocal.set(),初始化值,直接用的yyyyMMddHHmmssSSS
      import java.io.IOException;
      import javax.servlet.FilterChain;
      import javax.servlet.ServletException;
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      import org.springframework.stereotype.Component;
      import org.springframework.web.filter.OncePerRequestFilter;
      import com.copote.comb.manager.util.DateUtils;
      
      /**
       * 请求处理前生成批次号.
       *
       * @author rxz
       */
      @Component
      public class BatchNoFilter extends OncePerRequestFilter {
          @Override
          protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
                  throws ServletException, IOException {
              BatchNoUtils.add(Long.valueOf(DateUtils.dateTimeNow("yyyyMMddHHmmssSSS")));
              filterChain.doFilter(request, response);
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
    • 获取批次号
      在这里插入图片描述
    🐡测试结果

    在这里插入图片描述

    🐟地理位置的获取


    测试结果中,可以看到IP归属地,北京市,看了下代码,直接调用http://whois.pconline.com.cn/ipJson.jsp,联网就行,本来以为这种操作都是收费的呢,看来也有免费的接口
    在这里插入图片描述
    在这里插入图片描述

    • 测试类
    package com.renxiaozhao.demo;
    
    import java.io.BufferedReader;
    import java.io.InputStreamReader;
    import java.net.URL;
    import java.net.URLConnection;
    
    public class Test2 {
    
    	public static void main(String[] args) {
    	    getLocation("192.168.3.10");
    	    getLocation("36.99.142.234");
        }
    	private static String getLocation(String ip) {
    	    StringBuilder result = new StringBuilder();
            BufferedReader in = null;
            String url = "http://whois.pconline.com.cn/ipJson.jsp?";
            try {
                String param = "ip=" + ip + "&json=true";
                URL realUrl = new URL(url+param);
                URLConnection connection = realUrl.openConnection();
                connection.setRequestProperty("accept", "*/*");
                connection.setRequestProperty("connection", "Keep-Alive");
                connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
                connection.connect();
                in = new BufferedReader(new InputStreamReader(connection.getInputStream(), "GBK"));
                String line;
                while ((line = in.readLine()) != null) {
                    result.append(line);
                }
                System.out.println(result);
            } catch (Exception e) {
                System.out.println("获取IP归属地异常:" + e.getMessage());
            } finally {
                try {
                    if (in != null) {
                        in.close();
                    }
                } catch (Exception ex) {
                    System.out.println("关闭流异常:" + ex.getMessage());
                }
            }
            return result.toString();
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
  • 相关阅读:
    usb设备一直连接异常
    【2011】【论文笔记】用THz-TDS观察水树——
    Spark文章汇总
    韩语难学吗
    浅谈一下前端字符编码
    接口自动化和UI自动化的区别
    Sonar代码规则
    UVM-1.1学习(三)——`uvm_object_utils的本质
    【鸿蒙 HarmonyOS 4.0】通知
    09【C语言 & 趣味算法】再识:折半查找(二分查找):基本思想、程序流程图及完整代码、附:顺序查找
  • 原文地址:https://blog.csdn.net/qq_36434219/article/details/128061958