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

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

相关推荐
Victor35612 小时前
Hibernate(91)如何在数据库回归测试中使用Hibernate?
后端
Victor35612 小时前
MongoDB(1)什么是MongoDB?
后端
Victor35619 小时前
https://editor.csdn.net/md/?articleId=139321571&spm=1011.2415.3001.9698
后端
Victor35619 小时前
Hibernate(89)如何在压力测试中使用Hibernate?
后端
灰子学技术20 小时前
go response.Body.close()导致连接异常处理
开发语言·后端·golang
Gogo81621 小时前
BigInt 与 Number 的爱恨情仇,为何大佬都劝你“能用 Number 就别用 BigInt”?
后端
fuquxiaoguang21 小时前
深入浅出:使用MDC构建SpringBoot全链路请求追踪系统
java·spring boot·后端·调用链分析
毕设源码_廖学姐1 天前
计算机毕业设计springboot招聘系统网站 基于SpringBoot的在线人才对接平台 SpringBoot驱动的智能求职与招聘服务网
spring boot·后端·课程设计
野犬寒鸦1 天前
从零起步学习并发编程 || 第六章:ReentrantLock与synchronized 的辨析及运用
java·服务器·数据库·后端·学习·算法
逍遥德1 天前
如何学编程之01.理论篇.如何通过阅读代码来提高自己的编程能力?
前端·后端·程序人生·重构·软件构建·代码规范