本文目录导读:

我来提供一个完整的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;
}
}
}
这个完整的审计日志实现包括:
- 灵活的注解配置:通过自定义注解实现审计功能
- 完整的日志信息:记录用户、IP、操作、参数、响应等
- 异步处理:避免影响主业务流程
- 异常记录:记录失败操作和错误信息
- 查询能力:支持多条件组合查询
- 扩展性好:易于添加新的审计场景
使用时只需在需要审计的方法上添加@AuditLog注解即可。