SpringBoot+AOP+注解实现用户操作日志

使用SpringBoot记录用户操作日志 COMMENT ON COLUMN "SCOTT"."SYS_LOG"."CREATE_TIME" IS '创建时间';COMMENT ON COLUMN "SCOTT"."SYS_LOG"."OPERATION" IS '用户操作';COMMENT ON COLUMN "SCOTT"."SYS_LOG"."METHOD" IS '请求方法';在工作中我们可能会遇到一个需求,就是记录用户的操作信息,接下来使用spring的aop特性实现这一需求。二、自定义一个注解,用于标注需要监控的方法。 阅读详情

定义数据表:

CREATE TABLE `sys_oper_log` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) DEFAULT NULL,
  `title` varchar(255) DEFAULT NULL COMMENT '模块标题',
  `business_type` tinyint(4) DEFAULT NULL COMMENT '业务类型(1新增,2修改,3删除,4查询,5上传)',
  `url` varchar(255) DEFAULT NULL COMMENT '路径名称',
  `opt_param` varchar(5000) DEFAULT NULL COMMENT '参数',
  `status` tinyint(4) DEFAULT NULL COMMENT '操作状态(0正常 1异常)',
  `error_msg` varchar(255) DEFAULT NULL COMMENT '错误消息',
  `oper_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '操作时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=68 DEFAULT CHARSET=utf8mb4;

2.引入依赖:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>2.0.9</version>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

自定义日志注解:

@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OperationUserLog {
    // 自定义模块名
    String title() default "";
    // 方法传入的参数
    String optParam() default "";
    // 操作类型,eg:INSERT, UPDATE...
    BusinessType businessType() default BusinessType.OTHER;
}

枚举类:

public enum BusinessType {
    // 查找
    SELECT,
    // 新增
    INSERT,
    // 修改
    UPDATE,
    // 删除
    DELETE,
    //上传
    UPLOAD,
}

自定义切面:

/**
 * @author liz
 */
@Order(1)
@Aspect
@Component
@Log4j2
public class OperationLogAspect {
    @Autowired
    private SysOperLogService sysOperLogService;
    //定义切点 这个切点是注解类,通过 @annotation 来定义这个切点监控是注解类。
    @Pointcut("@annotation(com.*.*.annotation.OperationUserLog)")
    public void logPointCut() {}

    /**
     * 处理完请求后执行
     * @param joinPoint 切点
     */
    @AfterReturning(pointcut = "logPointCut()")
    public void doAfterReturning(JoinPoint joinPoint) {
        handleLog(joinPoint, null);
    }
    /**
     * 拦截异常操作
     * @param joinPoint 切点
     * @param e         异常
     */
    @AfterThrowing(value = "logPointCut()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Exception e) {
        handleLog(joinPoint, e);
    }

    private void handleLog(final JoinPoint joinPoint, final Exception e) {
        try {
            String adminName = "";
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (attributes != null) {
                HttpServletRequest request = attributes.getRequest();
                adminName = request.getAttribute("adminName")==null?"系统调用":request.getAttribute("adminName").toString();
            }
            OperationUserLog controllerLog = getAnnotationLog(joinPoint);
            if (controllerLog == null) {
                return;
            }
            SysOperLog operLog = new SysOperLog();
            operLog.setStatus(0);
            String uri = UserUrlUtils.getUri();
            operLog.setUrl(uri);
            operLog.setUsername(adminName);
            // 处理注解上的参数
            getControllerMethodDescription(joinPoint, controllerLog, operLog);
            // 保存数据库
            sysOperLogService.addOperlog(operLog.getTitle(), operLog.getBusinessType(), operLog.getUrl(), operLog.getStatus(), operLog.getOptParam(), operLog.getErrorMsg(),operLog.getUsername());
            }catch (Exception exception){
            log.error("操作日志保存异常"+exception);
            }
        }

    /**
     * 是否存在注解@OperationUserLog,如果存在就获取,不存在则返回null
     * @param joinPoint
     * @return
     */
    private OperationUserLog getAnnotationLog(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        if (method != null) {
            return method.getAnnotation(OperationUserLog.class);
        }
        return null;
    }

    /**
     * 获取Controller层上OperationUserLog注解中对方法的描述信息
     */
    private void getControllerMethodDescription(JoinPoint joinPoint, OperationUserLog myLog, SysOperLog operLog) {
        // 设置业务类型(0其它 1新增 2修改 3删除)
        operLog.setBusinessType(myLog.businessType().ordinal());
        // 设置模块标题
        operLog.setTitle(myLog.title());
        // 对方法上的参数进行处理,处理完:userName=xxx,password=xxx
        String optParam = getAnnotationValue(joinPoint, myLog.optParam());
        operLog.setOptParam(optParam);
    }

    /**
     * 对方法上的参数进行处理
     * @param joinPoint
     * @param name
     * @return
     */
    private String getAnnotationValue(JoinPoint joinPoint, String name) {
        String paramName = name;
        // 获取方法中所有的参数
        Map<String, Object> params = getParams(joinPoint);
        // 参数是否是动态的:#{paramName}
        if (paramName.matches("^#\\{[^}]*\\}(,#[^}]*\\})*$")) {
            // 获取参数名,去掉#{ }
            paramName = paramName.replace("#{", "").replace("}", "");
            // 是否是复杂的参数类型:对象.参数名
            if (paramName.contains(".")) {
                String[] split = paramName.split("\\.");
                // 获取方法中对象的内容
                Object object = getValue(params, split[0]);
                // 转换为JsonObject
                JSONObject jsonObject = (JSONObject) JSONObject.toJSON(object);
                if(jsonObject!=null){
                    // 获取值
                    Object o = jsonObject.get(split[1]);
                    return String.valueOf(o);
                }
            } else {// 简单的动态参数直接返回
                StringBuilder str = new StringBuilder();
                String[] paraNames = paramName.split(",");
                for (String paraName : paraNames) {
                    String val = String.valueOf(getValue(params, paraName));
                    // 组装成 userName=xxx,password=xxx,
                    str.append(paraName).append("=").append(val).append(",");
                }
                // 去掉末尾的,
                if (str.toString().endsWith(",")) {
                    return str.substring(0, str.length() - 1);
                } else {
                    return str.toString();
                }
            }
        }
        // 非动态参数直接返回
        return name;
    }

    /**
     * 获取方法上的所有参数,返回Map类型, eg: 键:"userName",值:xxx  键:"password",值:xxx
     * @param joinPoint
     * @return
     */
    public Map<String, Object> getParams(JoinPoint joinPoint) {
        Map<String, Object> params = new HashMap<>(8);
        // 通过切点获取方法所有参数值["zhangsan", "123456"]
        Object[] args = joinPoint.getArgs();
        // 通过切点获取方法所有参数名 eg:["userName", "password"]
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String[] names = signature.getParameterNames();
        for (int i = 0; i < args.length; i++) {
            params.put(names[i], args[i]);
        }
        return params;
    }

    /**
     * 从map中获取键为paramName的值,不存在放回null
     * @param map
     * @param paramName
     * @return
     */
    private Object getValue(Map<String, Object> map, String paramName) {
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            if (entry.getKey().equals(paramName)) {
                return entry.getValue();
            }
        }
        return "";
    }


}

拦截器

@Log4j2
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

    @Autowired
    private UrlInterceptor urlInterceptor;

    //可以多个并列拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(LoginInterceptor()).addPathPatterns("/**");
        registry.addInterceptor(urlInterceptor).addPathPatterns("/**");
    }

获取系统URL路径

public class UserUrlUtils {

    private static final ThreadLocal<String> uriHolder = new ThreadLocal<>();
    public static void setUri(String uri) {
        uriHolder.set(uri);
    }

    public static String getUri() {
        return uriHolder.get();
    }

    public static void clear() {
        uriHolder.remove();
    }

}

注解使用:参数形式:

    @ResponseBody
    @RequestMapping(value = "list", method = RequestMethod.GET)
    @OperationUserLog(title = "讲师-查询讲师列表", optParam = "#{name},#{address}", businessType = BusinessType.SELECT)
    public Result selectLecturerList(@RequestParam(value = "pageNo", defaultValue = "1", required = false) Integer pageNo,
                                     @RequestParam(value = "pageSize", defaultValue = "10", required = false) Integer pageSize,
                                     @RequestParam(value = "name", required = false) String name,
                                     @RequestParam(value = "address", required = false) String address
    ) {
        return Result.ok(list);
    }

对象形式:

@EnableXssFilter
    @ApiIgnore(value = "讲师-插入讲师信息")
    @ResponseBody
    @RequestMapping(value = "save", method = RequestMethod.POST)
    @OperationUserLog(title = "讲师-新增讲师", optParam = "#{lecturer}", businessType = BusinessType.INSERT)
    public Result saveLecturer(@RequestBody LecturerVo lecturer, HttpServletRequest request) {

结果:

有需要其他操作,需在自定义切面做业务操作。

SpringBoot AOP 实现埋点日志记录(完整源码) SpringBoot AOP 实现埋点日志记录(完整源码)一、Spring Boot AOP二、AOP的常用术语三、源码实现埋点日志记录3.1 项目结构图3.2 代码实现 随着互联网技术的深入发展,各个系统的日活用户、访问量、点击量成指数上升,为保证系统的安全性、易用性,每个系统都需要对用户的访问做埋点记录、跟踪,从而获取用户常用的操作习惯,同时也方便系统管理人员对系统做日常记录、跟踪。 一、Sp... 阅读详情

相关推荐

Springboot自定义注解+AOP实现日志管理

前言 在写Aurora这个项目之前,我是通过拦截器去记录用户操作日志,而这里讲解如何使用AOP配合自定义注解去实现用户日志记录,使用拦截器的方式,下一章贴出 导入依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-st...

qq_37976540的博客 895

spring boot 使用AOP+自定义注解+反射实现操作日志记录修改前数据和修改后对比数据,并保存至日志表

*** 写入日志表时,字段对应的中文注释*/@Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到@Target({ElementType.FIELD,ElementType.METHOD})//定义注解的作用目标**作用范围字段、枚举的常量/方法@Documented //说明该注解将被包含在javadoc中/*** 汉字全称* @return*/

yuechuzhixing的博客 4543

C/C++ 计算程序运行的时间

C/C++计算程序执行时间的方法,精度依次增加,推荐度也依次增加 1.#include <time.h> clock_t start, finish; double process_interval ; start=clock(); process(); //要计时的程序 finish=clock(); process_interval =(double)(finish-star...

stu_shanghui的博客 1020

Spring Boot + Aop 记录用户操作日志

本文主要介绍通过AOP记录用户操作日志,这也是目前比较常用的用法,由于水平有限,所以可能存在错漏之处,望指正。

xmg_zs的博客 6966

VBA学习笔记6:将多个工作表中满足条件的数据汇总到同一个工作表

VBA学习笔记6:将多个工作表中满足条件的数据汇总到同一个工作表

weixin_45355881的博客 4708

保姆级避坑指南:在Ubuntu 22.04上从零编译Autoware.universe(含ROS2 Humble和CUDA配置)

本文提供了一份详细的Autoware.universe编译指南,涵盖Ubuntu 22.04系统优化、ROS2 Humble安装避坑、CUDA配置及Demo运行技巧。通过分步操作和实战经验,帮助开发者高效完成环境搭建,避免常见错误,提升自动驾驶开发效率。

weixin_30642561的博客 350

SpringBoot实战(六)自定义注解 + AOP 记录接口访问日志

目录一、AOP的简单介绍1. AOP是什么2. AOP能做什么3. 切面执行顺序4. AOP注解二、实战代码1. 依赖引入2. AOP示例3. 定义接口,进行测试4. 源码地址 一、AOP的简单介绍 1. AOP是什么 AOP(Aspect Oriented Programming)–面向切面编程 通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。 实现方式:JDK动态代理、CGLIB代理。前者基于接口,后者基于子类。 2. AOP能做什么 统计接口

ACGkaka的博客 4336

实战复盘:一次绕过WAF的EDUSRC SQL注入漏洞挖掘

本文详细复盘了一次针对教育行业网站的SQL注入漏洞挖掘实战。通过手动信息收集发现可疑点,并利用‘+’号替换空格等技巧,成功绕过WAF(Web应用防火墙)的拦截。文章逐步演示了从判断注入点到手工获取数据结构、最终解密进入后台的完整过程,并总结了手工测试的优势及高级绕过思路,强调了在EDUSRC等授权范围内进行安全测试的重要性。

weixin_28622215的博客 191

springboot通过自定义注解和AOP来实现业务日志记录的功能

外链:访问地址 1、首先是环境 我使用的是spring-boot构建的项目,通过自定义注解,aop来实现业务日志的记录 2、pom引入aop模块 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</...

a7847的博客 560

LLaVA-1.5:多模态视觉问答新突破

LLaVA-1.5在多模态视觉问答(VQA)任务中取得了显著进展,主要得益于视觉-语言连接器的优化和特定任务数据集的增加。通过将连接器升级为两层MLP,模型在处理视觉与语言关系时表现更佳。同时,引入如OKVQA、OCRVQA等数据集,增强了模型在复杂视觉推理任务中的能力。在11个基准测试中,LLaVA-1.5超越了现有方法,展示了其强大的多模态能力。此外,模型通过提升输入图像分辨率、扩展语言模型和改进多语言能力,进一步提升了性能。尽管在短格式回答任务中初期表现不佳,但通过调整提示格式和微调,其性能显著提升

weixin_57060548的博客 945

基于共识的捆绑算法CBBA在多无人机多任务调度中的实现

在捆绑过程中,每个无人机根据自己的任务清单和其他无人机的任务清单,确定一个被称为“束”的集合。通过合理的任务分配和无人机协作,CBBA算法能够实现最优的任务调度,提高了多无人机系统的效率和执行能力。在每一轮迭代中,无人机根据自己的当前任务和位置信息将任务清单发送给其他无人机,并接收其他无人机发送过来的任务清单。通过比较其他无人机的任务清单和自身的任务清单,每个无人机可以更新自己的任务分配情况。在这一步中,每个无人机根据自己选择的新任务,更新自身的任务清单,并将更新后的清单发送给其他无人机。

持续更新 1156

基于SpringBoot使用AOP切面使用注解实现用户操作日志管理

先介绍几个注解 @Aspect //代表这个类是一个切面 @Component //代表这个类被Spring所管理 @Before //这个代表前置通知运行方法前拦截运行有这个注解的方法 @After //这个代表后置通知运行方法后返回拦截运行有这个注解的方法 下面开始讲解使用方法 首先添加Spring-boot的AOP jar包 <!-- aop依赖 --> <depend...

w167837978的博客 1126

Python爬取谷歌学术全攻略:基于Playwright与Asyncio的高效、抗反爬虫实践

同时,文章将详细解析爬虫过程中可能遇到的各种挑战,如IP封锁、验证码、动态内容加载等,并提供切实可行的解决方案。self.logger.info(f"第 {current_page} 页解析完成,共找到 {len(page_results)} 条结果。:谷歌学术的HTML结构并非一成不变,其CSS类名和标签结构可能会发生变化,且不同地区、不同语言的界面可能存在差异,增加了数据解析的难度。self.logger.info(f"开始搜索关键词: '{keyword}',计划爬取 {max_pages} 页")

2201_76125261的博客 1550

springboot—spring aop 实现系统操作日志记录存储到数据库

springboot—spring aop 实现系统操作日志记录存储到数据库1. 需要引用的依赖2. 创建实体类3. 使用spring 的 aop 技术切到自定义注解上,所以先创建一个自定义注解类4. 创建aop切面实现类5. 接下来就可以在需要监控的方法上添加 aop的自定义注解格式为 @+自定义注解的类名 @MyLog6. 执行上面的方法操作后,会将记录保存到数据库涉及到的util类: 原文:...

qq_25125327的博客 3075

如何用libwebsocket库在stm32F407上实现websocket客户端?

🏆本文收录于 《全栈 Bug 调优(实战版)》 专栏。专栏聚焦真实项目中的各类疑难 Bug,从成因剖析 → 排查路径 → 解决方案 → 预防优化全链路拆解,形成一套可复用、可沉淀的实战知识体系。无论你是初入职场的开发者,还是负责复杂项目的资深工程师,都可以在这里构建一套属于自己的「问题诊断与性能调优」方法论,助你稳步进阶、放大技术价值 。

**My Coding Family** 1135

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

Spring Boot AOP记录用户操作日志

Spring Boot AOP记录用户操作日志 在Spring框架中,使用AOP配合自定义注解可以方便的实现用户操作的监控。首先搭建一个基本的Spring Boot Web环境开启Spring Boot,然后引入必要依赖: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <d...

li_jiazhi的博客 2158

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

SpringBoot 使用自定义注解(基于AOP) 实现操作日志管理

操作日志的记录 因为我们不光要记录代码的运行,如(logback log4j),而且还应该记录用户的行为(Controller 请求日志),这叫做业务运行日志。 业务运行日志的作用 记录用户的行为 用于后续的分析 记录用户的所有的操作 日志实现的思路 1.我们需要记录哪些数据(谁在什么时间干了什么事情,结果如何)? 数据要存入到哪里(存储到数据库表,推送到指定日志存储服务,打印到日志文件等)? 2.在项目中什么位置记录(一般在Controller请求,进行around 环绕增强) 如何实现记录

最近学习目标:MySQL,Redis 2084

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

基于Spring Aop实现的用户操作记录

引言 众所周知,spring aop是基于动态代理模式的,而动态代理又根据是否实现接口分为两类:1.基于JDK的动态代理和基于chlib的动态代理,下面就两者的区别做些简单概括(如所示):https://github.com/TomasXiong/design-pattern.git 本文基于spring boot实现一个简单的用户日志拦截记录,记录用户的操作模块和操作时间。 首先,第一步,建议日...

xiongminming的博客 980

SpringBoot AOP记录用户操作日志

Aop记录用户操作日志相关依赖 <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjrt --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.9.5</version> </depe

一页书的博客 1749

java 用户操作日志_SpringBoot+AOP实现用户操作日志的记录

前言:任何一个项目都会有一个用户操作日志(也叫行为日志)的模块,它主要用来记录某个用户做了某个操作,当出现操作失败时,通过日志就可以快速的查找是哪个用户在哪个模块出现了错误,以便于开发人员快速定位问题所在。实现这一功能一般有两种方法:第一种就是很传统的做法,就是在每个模块进行插入日志的操作(不推荐),这种做法虽然实现了记录用户的操作,但很繁琐而且基本上是重复的工作。第二种就是使用Spring的AO...

weixin_35207054的博客 1854

springBoot,AOP切面实现日志记录,自定义注解,注解属性动态传参

SpringBoot,AOP切面实现日志记录,自定义注解,注解属性动态传参 项目需求: 需要记录用户的操作具体的方法,记录当前具体操作的是哪一条数据信息,作为参数也一并保存起来。 如:更新数据,IP,电脑号,调用方法,类型为修改,及这条更新的数据具体信息。 用SpringAOP + Annotation来实现 自定义aop注解类,通用日志文件 import java.lang.annotation.*; /****************************** * 用途说明: 通用日志文件

weixin_43931918的博客 3753

Spring Boot操作日志(AOP+注解)项目实战

Spring Boot操作日志(AOP+注解)项目实战

wahahahq的博客 879

java aop日志保存_Spring Boot AOP记录用户操作日志

在Spring框架中,使用AOP配合自定义注解可以方便的实现用户操作的监控。首先搭建一个基本的Spring Boot Web环境开启Spring Boot,然后引入必要依赖:org.springframework.bootspring-boot-starter-jdbcorg.springframework.bootspring-boot-starter-aopcom.oracleojdbc66....

weixin_28358083的博客 395

Spring boot学习(六)Spring boot实现AOP记录操作日志

前言 在实际的项目中,特别是管理系统中,对于那些重要的操作我们通常都会记录操作日志。比如对数据库的CRUD操作,我们都会对每一次重要的操作进行记录,通常的做法是向数据库指定的日志表中插入一条记录。这里就产生了一个问题,难道要我们每次在 CRUD的时候都手动的插入日志记录吗?这肯定是不合适的,这样的操作无疑是加大了开发量,而且不易维护,所以实际项目中总是利用AOP(Aspect Oriented ...

java1856905的博客 1085

Spring boot实现AOP记录操作日志

前言 在实际的项目中,特别是管理系统中,对于那些重要的操作我们通常都会记录操作日志。比如对数据库的CRUD操作,我们都会对每一次重要的操作进行记录,通常的做法是向数据库指定的日志表中插入一条记录。这里就产生了一个问题,难道要我们每次在 CRUD的时候都手动的插入日志记录吗?这肯定是不合适的,这样的操作无疑是加大了开发量,而且不易维护,所以实际项目中总是利用AOP(Aspect Oriented P...

zhuguang10的博客 6751
上一篇: MySQL反范式化
下一篇: 布隆过滤器原理和使用
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值