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

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

相关推荐
止语Lab3 小时前
好的 DX 不等于少写代码——三种语言的摩擦力设计课
后端
吃饱了得干活3 小时前
别再手动解析 LLM 输出了!LangChain 四种结构化输出方案对比
后端·python·langchain
程序员天天困3 小时前
Arthas trace 命令怎么用?一行定位最慢那行代码
jvm·后端
Huiturn3 小时前
GPT 5.6 连续编码 10 小时,纯 Python 啃下 Word 二进制格式——doc2docx 实现拆解
后端
用户298698530143 小时前
Python 实现 Excel 与 Markdown 互转的实用指南
后端·python·excel
用户77283104908403 小时前
krono-job:零侵入、单二进制交付的分布式任务调度平台(开源)
后端
Conan在掘金3 小时前
鸿蒙报错速查:Function lacks ending return statement,返回路径漏 return 就炸,根因 + 真解法
后端
人间凡尔赛4 小时前
AI-Native 云原生架构:2026 年从容器编排到智能体编排的范式革命
后端·云原生·架构
IT_陈寒4 小时前
Vue的响应式让我加班到凌晨3点,原来问题出在这
前端·人工智能·后端
卷无止境4 小时前
提升 Python 代码健壮性的方法大盘点
后端·python