如何精确记录用户操作行为?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. 性能监控:可以扩展注解,添加超时警告功能

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

相关推荐
Java微观世界10 小时前
别再混淆!Java抽象类与接口的终极对比(语法+设计+Java 8+新特性)
后端
沉默王二11 小时前
金山还是小米,谁才是雷军的亲儿子?附小米线下一面面经(八股盛宴)
后端·面试
Aurora_NeAr12 小时前
Kubernetes权威指南-原理篇
后端·云原生
Java水解12 小时前
MySQL 新增字段但 Java 实体未更新:潜在问题与解决方案
后端·mysql
Java水解12 小时前
Kafka事务:构建可靠的分布式消息处理系统
后端·kafka
京茶吉鹿12 小时前
三步构建完美树节点,从此告别数据结构焦虑!
java·后端
ZZHHWW12 小时前
高可用架构实战指南:告别半夜被叫醒的噩梦
后端·架构
用户40993225021212 小时前
PostgreSQL 17安装总翻车?Windows/macOS/Linux避坑指南帮你搞定?
后端·ai编程·trae
橙序员小站13 小时前
搞定系统设计题:如何设计一个订单系统?
java·后端·面试
IT_陈寒14 小时前
React 18新特性全解析:这5个隐藏API让你的性能飙升200%!
前端·人工智能·后端