如何精确记录用户操作行为?Spring AOP实现日志审计方案

在我们平常开发的时候,肯定会遇到比如bug呀这类的问题,有时候报错会直接出现在控制台,有时候可能不会,所以我们查找错误的时候是非常麻烦的,今天就来说说如何使用Spring AOP实现一个优雅的日志审计方案。

一、为什么需要记录用户操作?

想象一下,假如系统出现异常数据,如果没有操作日志,可能要花费大量时间翻看代码和数据库变化。而如果有详细的操作记录,就能快速定位到是谁、在什么时间、执行了什么操作,问题自然迎刃而解。

传统的做法是在每个方法里手动写日志代码,但是这样很麻烦的,而且满篇都是这种看着都烦。比如:

java 复制代码
public void updateUser(User user) {
    // 业务逻辑
    log.info("用户ID:{}修改了用户信息", getUserID());
    // 更多业务代码
}

每个方法都这样写,不仅繁琐,还会让业务代码变得臃肿。这时候,我们就需要改变思路。

二、Spring AOP?

概念就不多说了,都知道就是像一个"拦截器",可以在方法执行前后插入自定义逻辑。

Spring AOP提供了四种类型的通知:

  • 前置通知:在方法执行前执行
  • 后置通知:在方法执行后执行
  • 异常通知:在方法抛出异常时执行
  • 环绕通知:可以在方法执行前后自定义行为

三、具体实现步骤

1. 添加依赖

首先在pom.xml中添加Spring AOP依赖:

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2. 创建自定义注解

java 复制代码
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AuditLog {
    String value() default "";
    String module() default ""; // 模块名称
    String operation() default ""; // 操作类型
}

3. 实现切面逻辑

java 复制代码
@Aspect
@Component
public class AuditLogAspect {
    
    @Autowired
    private HttpServletRequest request;
    
    // 定义切点:所有带有@AuditLog注解的方法
    @Pointcut("@annotation(com.example.demo.annotation.AuditLog)")
    public void auditPointCut() {}
    
    @Around("auditPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        long beginTime = System.currentTimeMillis();
        
        // 执行方法
        Object result = point.proceed();
        
        // 执行时长
        long time = System.currentTimeMillis() - beginTime;
        
        // 保存日志
        saveLog(point, time, result);
        
        return result;
    }
    
    private void saveLog(ProceedingJoinPoint joinPoint, long time, Object result) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        
        AuditLog auditLog = method.getAnnotation(AuditLog.class);
        
        // 获取请求信息
        String username = getUsername();
        String ip = getClientIP();
        String methodName = method.getName();
        String params = getParams(joinPoint);
        
        // 构建日志实体
        Log log = new Log();
        log.setUsername(username);
        log.setIp(ip);
        log.setMethod(methodName);
        log.setParams(params);
        log.setTime(time);
        log.setOperation(auditLog.operation());
        log.setModule(auditLog.module());
        log.setCreateTime(new Date());
        
        // 保存到数据库
        logRepository.save(log);
    }
    
    private String getUsername() {
        // 从安全上下文中获取当前用户
        // 实际项目中可根据使用的安全框架调整
        return "当前用户名";
    }
    
    private String getClientIP() {
        // 获取客户端IP
        return request.getRemoteAddr();
    }
    
    private String getParams(ProceedingJoinPoint point) {
        // 获取方法参数
        Object[] args = point.getArgs();
        if (args == null || args.length == 0) {
            return "";
        }
        try {
            return JSON.toJSONString(args);
        } catch (Exception e) {
            return "参数序列化失败";
        }
    }
}

4. 使用注解记录日志

在需要记录日志的方法上添加注解:

java 复制代码
@AuditLog(module = "用户管理", operation = "更新用户信息")
public void updateUser(User user) {
    // 业务逻辑
}

之前的写法呢是所有的请求接口都进行拦截,并且进行日志记录的,如下图

这种写法就是直接将所有只要是这个controller下的接口全部都进行数据库记录了,数据库日志表多的能撑死。

这种方式就简单了,哪个接口需要就给哪个接口加上注解进行记录就行。

四、优化建议

  1. 异步记录日志:日志记录不应该影响主业务流程,可以使用@Async实现异步保存

  2. 参数过滤:敏感参数(如密码)不应该记录到日志中,需要做过滤处理

  3. 日志查询:提供按时间、操作人、模块等条件查询日志的功能

  4. 性能监控:可以扩展注解,添加超时警告功能

但是呢不是说日志越好越多,啥接口都给打上日志,这种方式是不可取的,我们要在满足需求的情况下进行日志管理的操作!

相关推荐
jakeswang7 分钟前
Jenkins 已成过去式!新兴替代工具GitHub Actions即将崛起
后端·jenkins·github actions
合作小小程序员小小店8 分钟前
大屏开发,在线歌词舆情分析系统demo,基于python,flask,web,echart,nlp,自然语言数据库mysql。
后端·python·flask·nlp·echarts
武子康44 分钟前
大数据-138 ClickHouse MergeTree 实战详解|分区裁剪 × 稀疏主键索引 × marks 标记 × 压缩
大数据·后端·nosql
非凡ghost1 小时前
图吧工具箱-电脑硬件圈的“瑞士军刀”
前端·javascript·后端
非凡ghost1 小时前
Xrecode3(多功能音频转换工具)
前端·javascript·后端
非凡ghost1 小时前
Subtitle Edit(字幕编辑软件) 中文绿色版
前端·javascript·后端
扎瓦斯柯瑞迫1 小时前
cursor: 10分钟魔改环境、优雅获取Token
前端·javascript·后端
天天摸鱼的java工程师1 小时前
领导:“线程池又把服务器搞崩了!” 八年 Java 开发:按业务 + 服务器配,从此稳抗大促
java·后端
紫穹1 小时前
010.ConversationChain 一键记忆链:字幕版实现与暴躁助手实战
后端·ai编程
非凡ghost2 小时前
Flameshot(开源免费的截图工具) 中文绿色版
前端·javascript·后端