Java实现审计日志案例

wen java案例 1

本文目录导读:

Java实现审计日志案例

  1. 基于Spring AOP的审计日志实现
  2. 使用方法
  3. 高级特性扩展

我来提供一个完整的Java审计日志实现案例,包含多个实现方案。

基于Spring AOP的审计日志实现

依赖配置 (pom.xml)

<dependencies>
    <!-- Spring Boot Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- Spring AOP -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    <!-- Spring Data JPA -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <!-- MySQL -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <!-- Lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <!-- Jackson -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
    </dependency>
</dependencies>

审计日志注解

package com.example.audit.annotation;
import java.lang.annotation.*;
/**
 * 审计日志注解
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AuditLog {
    /** 操作类型 */
    String operation() default "";
    /** 模块名称 */
    String module() default "";
    /** 操作描述 */
    String description() default "";
    /** 是否记录请求参数 */
    boolean recordRequest() default true;
    /** 是否记录响应结果 */
    boolean recordResponse() default false;
}

审计日志实体

package com.example.audit.entity;
import lombok.Data;
import javax.persistence.*;
import java.time.LocalDateTime;
@Data
@Entity
@Table(name = "audit_log")
public class AuditLogEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    /** 操作人ID */
    @Column(name = "user_id")
    private Long userId;
    /** 操作人名称 */
    @Column(name = "username")
    private String username;
    /** 操作IP */
    @Column(name = "ip")
    private String ip;
    /** 模块 */
    @Column(name = "module")
    private String module;
    /** 操作类型 */
    @Column(name = "operation")
    private String operation;
    /** 方法描述 */
    @Column(name = "description")
    private String description;
    /** 请求参数 */
    @Column(name = "request_params", columnDefinition = "TEXT")
    private String requestParams;
    /** 响应结果 */
    @Column(name = "response_result", columnDefinition = "TEXT")
    private String responseResult;
    /** 执行时间(毫秒) */
    @Column(name = "execution_time")
    private Long executionTime;
    /** 是否成功 */
    @Column(name = "success")
    private Boolean success;
    /** 异常信息 */
    @Column(name = "error_message", columnDefinition = "TEXT")
    private String errorMessage;
    /** 创建时间 */
    @Column(name = "create_time")
    private LocalDateTime createTime;
}

审计日志仓储

package com.example.audit.repository;
import com.example.audit.entity.AuditLogEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository;
@Repository
public interface AuditLogRepository extends JpaRepository<AuditLogEntity, Long>, 
        JpaSpecificationExecutor<AuditLogEntity> {
}

审计日志AOP切面

package com.example.audit.aspect;
import com.example.audit.annotation.AuditLog;
import com.example.audit.entity.AuditLogEntity;
import com.example.audit.repository.AuditLogRepository;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.stream.Collectors;
@Aspect
@Component
public class AuditLogAspect {
    @Autowired
    private AuditLogRepository auditLogRepository;
    @Autowired
    private ObjectMapper objectMapper;
    @Around("@annotation(auditLog)")
    public Object around(ProceedingJoinPoint joinPoint, AuditLog auditLog) throws Throwable {
        long startTime = System.currentTimeMillis();
        AuditLogEntity logEntity = new AuditLogEntity();
        try {
            // 设置基本信息
            logEntity.setModule(auditLog.module());
            logEntity.setOperation(auditLog.operation());
            logEntity.setDescription(auditLog.description());
            logEntity.setCreateTime(LocalDateTime.now());
            // 获取当前用户信息
            setUserInfo(logEntity);
            // 记录请求参数
            if (auditLog.recordRequest()) {
                String requestParams = getRequestParams(joinPoint);
                logEntity.setRequestParams(requestParams);
            }
            // 执行目标方法
            Object result = joinPoint.proceed();
            // 记录执行时间
            long executionTime = System.currentTimeMillis() - startTime;
            logEntity.setExecutionTime(executionTime);
            logEntity.setSuccess(true);
            // 记录响应结果
            if (auditLog.recordResponse()) {
                logEntity.setResponseResult(objectMapper.writeValueAsString(result));
            }
            return result;
        } catch (Throwable throwable) {
            // 记录异常信息
            long executionTime = System.currentTimeMillis() - startTime;
            logEntity.setExecutionTime(executionTime);
            logEntity.setSuccess(false);
            logEntity.setErrorMessage(throwable.getMessage());
            throw throwable;
        } finally {
            // 异步保存审计日志
            saveAuditLogAsync(logEntity);
        }
    }
    private void setUserInfo(AuditLogEntity logEntity) {
        try {
            HttpServletRequest request = getHttpServletRequest();
            if (request != null) {
                // 从请求中获取用户信息(根据实际认证方式修改)
                logEntity.setUsername(request.getHeader("X-User-Name"));
                logEntity.setUserId(Long.valueOf(request.getHeader("X-User-Id")));
                logEntity.setIp(getIpAddress(request));
            }
        } catch (Exception e) {
            logEntity.setUsername("anonymous");
            logEntity.setIp("unknown");
        }
    }
    private HttpServletRequest getHttpServletRequest() {
        ServletRequestAttributes attributes = 
            (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        return attributes != null ? attributes.getRequest() : null;
    }
    private String getIpAddress(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }
    private String getRequestParams(ProceedingJoinPoint joinPoint) {
        try {
            Object[] args = joinPoint.getArgs();
            // 过滤掉HttpServletRequest、HttpServletResponse等对象
            Object[] filteredArgs = Arrays.stream(args)
                .filter(arg -> !(arg instanceof HttpServletRequest) 
                    && !(arg instanceof HttpServletResponse)
                    && !(arg instanceof MultipartFile))
                .collect(Collectors.toList())
                .toArray();
            return objectMapper.writeValueAsString(filteredArgs);
        } catch (Exception e) {
            return "Failed to serialize request params: " + e.getMessage();
        }
    }
    private void saveAuditLogAsync(AuditLogEntity logEntity) {
        new Thread(() -> {
            auditLogRepository.save(logEntity);
        }).start();
    }
}

使用示例 - Service

package com.example.audit.service;
import com.example.audit.annotation.AuditLog;
import com.example.audit.entity.User;
import com.example.audit.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
    @AuditLog(
        module = "用户管理",
        operation = "CREATE",
        description = "创建用户",
        recordRequest = true,
        recordResponse = false
    )
    public User createUser(User user) {
        // 业务逻辑
        return userRepository.save(user);
    }
    @AuditLog(
        module = "用户管理",
        operation = "UPDATE",
        description = "更新用户信息"
    )
    public User updateUser(User user) {
        // 业务逻辑
        return userRepository.save(user);
    }
    @AuditLog(
        module = "用户管理",
        operation = "DELETE",
        description = "删除用户"
    )
    public void deleteUser(Long id) {
        // 业务逻辑
        userRepository.deleteById(id);
    }
    @AuditLog(
        module = "用户管理",
        operation = "QUERY",
        description = "查询用户列表",
        recordResponse = true
    )
    public List<User> findUsers(String keyword) {
        // 业务逻辑
        return userRepository.findByUsernameContaining(keyword);
    }
}

Controller示例

package com.example.audit.controller;
import com.example.audit.entity.User;
import com.example.audit.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/users")
public class UserController {
    @Autowired
    private UserService userService;
    @PostMapping
    public User createUser(@RequestBody User user) {
        return userService.createUser(user);
    }
    @PutMapping("/{id}")
    public User updateUser(@PathVariable Long id, @RequestBody User user) {
        user.setId(id);
        return userService.updateUser(user);
    }
    @DeleteMapping("/{id}")
    public void deleteUser(@PathVariable Long id) {
        userService.deleteUser(id);
    }
    @GetMapping
    public List<User> findUsers(@RequestParam(required = false) String keyword) {
        return userService.findUsers(keyword);
    }
}

审计日志查询Service

package com.example.audit.service;
import com.example.audit.entity.AuditLogEntity;
import com.example.audit.repository.AuditLogRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import javax.persistence.criteria.Predicate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@Service
public class AuditLogQueryService {
    @Autowired
    private AuditLogRepository auditLogRepository;
    public List<AuditLogEntity> queryAuditLogs(AuditLogQueryParam param) {
        Specification<AuditLogEntity> specification = (root, query, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            // 按用户名查询
            if (param.getUsername() != null && !param.getUsername().isEmpty()) {
                predicates.add(criteriaBuilder.like(
                    root.get("username"), "%" + param.getUsername() + "%"));
            }
            // 按模块查询
            if (param.getModule() != null && !param.getModule().isEmpty()) {
                predicates.add(criteriaBuilder.equal(
                    root.get("module"), param.getModule()));
            }
            // 按操作类型查询
            if (param.getOperation() != null && !param.getOperation().isEmpty()) {
                predicates.add(criteriaBuilder.equal(
                    root.get("operation"), param.getOperation()));
            }
            // 按时间范围查询
            if (param.getStartTime() != null) {
                predicates.add(criteriaBuilder.greaterThanOrEqualTo(
                    root.get("createTime"), param.getStartTime()));
            }
            if (param.getEndTime() != null) {
                predicates.add(criteriaBuilder.lessThanOrEqualTo(
                    root.get("createTime"), param.getEndTime()));
            }
            // 按成功状态查询
            if (param.getSuccess() != null) {
                predicates.add(criteriaBuilder.equal(
                    root.get("success"), param.getSuccess()));
            }
            return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
        };
        return auditLogRepository.findAll(specification);
    }
}

查询参数类

package com.example.audit.service;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class AuditLogQueryParam {
    private String username;
    private String module;
    private String operation;
    private LocalDateTime startTime;
    private LocalDateTime endTime;
    private Boolean success;
}

配置类

package com.example.audit.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration
@EnableAsync
@EnableScheduling
@EnableTransactionManagement
public class AppConfig {
}

使用方法

在Service方法上添加注解

@Service
public class OrderService {
    @AuditLog(
        module = "订单管理",
        operation = "CREATE",
        description = "创建订单",
        recordRequest = true
    )
    public Order createOrder(Order order) {
        // 业务逻辑
    }
}

查询审计日志

@RestController
@RequestMapping("/api/audit")
public class AuditController {
    @Autowired
    private AuditLogQueryService auditLogService;
    @GetMapping("/logs")
    public List<AuditLogEntity> queryLogs(@RequestBody AuditLogQueryParam param) {
        return auditLogService.queryAuditLogs(param);
    }
}

数据库表结构

CREATE TABLE audit_log (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    user_id BIGINT,
    username VARCHAR(50),
    ip VARCHAR(50),
    module VARCHAR(100),
    operation VARCHAR(50),
    description VARCHAR(500),
    request_params TEXT,
    response_result TEXT,
    execution_time BIGINT,
    success TINYINT(1),
    error_message TEXT,
    create_time DATETIME
);
-- 创建索引
CREATE INDEX idx_audit_time ON audit_log(create_time);
CREATE INDEX idx_audit_user ON audit_log(username);
CREATE INDEX idx_audit_module ON audit_log(module);

高级特性扩展

异步处理配置优化

@Configuration
@EnableAsync
public class AsyncConfig {
    @Bean("auditLogExecutor")
    public Executor auditLogExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(2);
        executor.setMaxPoolSize(5);
        executor.setQueueCapacity(1000);
        executor.setThreadNamePrefix("audit-log-");
        executor.initialize();
        return executor;
    }
}

敏感信息脱敏

public class DataDesensitizationUtil {
    public static String desensitize(String data, String type) {
        if (data == null || data.isEmpty()) {
            return data;
        }
        switch (type) {
            case "password":
                return "******";
            case "phone":
                return data.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
            case "email":
                return data.replaceAll("(\\w{3})\\w+(@\\w+\\.[a-z]+)", "$1***$2");
            default:
                return data;
        }
    }
}

这个完整的审计日志实现包括:

  1. 灵活的注解配置:通过自定义注解实现审计功能
  2. 完整的日志信息:记录用户、IP、操作、参数、响应等
  3. 异步处理:避免影响主业务流程
  4. 异常记录:记录失败操作和错误信息
  5. 查询能力:支持多条件组合查询
  6. 扩展性好:易于添加新的审计场景

使用时只需在需要审计的方法上添加@AuditLog注解即可。

抱歉,评论功能暂时关闭!