网站首页/ 信息中心/ 档案百科/

档案管理系统审计不严格?教你五步搭建严格审计体系

发布时间:2026年09月15日 06:00:28 浏览量:0

第一步:构建审计日志存储表

要实现严格的审计,首先需要将所有操作痕迹“落地”。这里我们使用MySQL作为存储介质,设计一张涵盖人员、时间、IP、操作模块、参数及结果的宽表。请直接在数据库中执行以下SQL语句:

```sql CREATE DATABASE IF NOT EXISTS archive_system DEFAULT CHARSET utf8mb4; USE archive_system; CREATE TABLE `sys_audit_log` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID', `module` varchar(50) NOT NULL COMMENT '操作模块:如档案管理、用户权限', `operation` varchar(100) NOT NULL COMMENT '操作名称:如下载档案、删除记录', `method` varchar(200) DEFAULT NULL COMMENT '执行方法的全路径', `params` longtext COMMENT '请求参数(JSON格式)', `ip` varchar(50) DEFAULT NULL COMMENT '请求IP地址', `username` varchar(50) DEFAULT NULL COMMENT '操作人账号', `status` tinyint(1) DEFAULT 1 COMMENT '操作状态:1成功,0失败', `error_msg` varchar(500) DEFAULT NULL COMMENT '失败时的错误信息', `cost_time` bigint(20) DEFAULT NULL COMMENT '执行耗时(毫秒)', `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '操作时间', PRIMARY KEY (`id`), KEY `idx_username_time` (`username`, `create_time`), KEY `idx_module` (`module`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统操作审计日志表'; ```

第二步:引入核心依赖与基础配置

基于Spring Boot 2.x环境,我们需要引入AOP(切面编程)来无侵入式地拦截操作,以及JSON处理工具。在你的pom.xml中添加以下依赖:

```xml org.springframework.boot spring-boot-starter-aop org.springframework.boot spring-boot-starter-web org.projectlombok lombok true com.fasterxml.jackson.core jackson-databind com.baomidou mybatis-plus-plus-boot-starter 3.5.3.1 mysql mysql-connector-java runtime ```

application.yml中配置数据库连接信息,确保能连接到第一步创建的数据库:

```yaml spring: datasource: url: jdbc:mysql://localhost:3306/archive_system?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: your_password driver-class-name: com.mysql.cj.jdbc.Driver ```

第三步:定义审计注解与枚举

为了标记哪些方法需要被审计,我们需要创建一个自定义注解@AuditLog。同时,为了保证数据安全,必须对敏感字段(如身份证号、密码)进行脱敏处理。

创建注解类AuditLog.java

```java package com.example.archive.annotation; import java.lang.annotation.; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface AuditLog { String value() default ""; // 操作名称,如 "删除档案" String module() default ""; // 模块名称,如 "档案管理" } ```

接着,创建一个简单的工具类DesensitizedUtil.java用于处理敏感数据:

```java package com.example.archive.util; import java.util.regex.Pattern; public class DesensitizedUtil { // 手机号脱敏 private static final Pattern PHONE_PATTERN = Pattern.compile("(?<=\\d{3})\\d{4}(?=\\d{4})"); // 身份证脱敏 private static final Pattern ID_CARD_PATTERN = Pattern.compile("(?<=\\d{6})\\d{8}(?=\\d{4})"); public static String desensitize(String content) { if (content == null) return null; content = PHONE_PATTERN.matcher(content).replaceAll(""); content = ID_CARD_PATTERN.matcher(content).replaceAll(""); return content; } } ```

第四步:实现核心AOP切面逻辑(关键)

这是整个系统的核心。切面会拦截带有@AuditLog的方法,提取上下文信息,捕获异常,并记录执行时间。

4.1 获取IP与用户信息工具类

在切面中使用前,先准备好获取真实IP的工具类IpUtils.java

```java package com.example.archive.util; import javax.servlet.http.HttpServletRequest; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; public class IpUtils { public static String getIpAddr() { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); String ip = request.getHeader("x-forwarded-for"); if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("WL-Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); } return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip; } // 模拟获取当前登录用户,实际项目中请从SecurityContext或Token中获取 public static String getCurrentUsername() { // TODO: 替换为真实的用户获取逻辑 return "admin"; } } ```

4.2 切面核心代码

创建AuditLogAspect.java

```java package com.example.archive.aspect; import com.example.archive.annotation.AuditLog; import com.example.archive.entity.SysAuditLog; import com.example.archive.service.AsyncLogService; import com.example.archive.util.DesensitizedUtil; import com.example.archive.util.IpUtils; import com.fasterxml.jackson.databind.ObjectMapper; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.lang.reflect.Method; @Aspect @Component public class AuditLogAspect { @Autowired private AsyncLogService asyncLogService; @Autowired private ObjectMapper objectMapper; // 定义切点:拦截所有带有 @AuditLog 注解的方法 @Pointcut("@annotation(com.example.archive.annotation.AuditLog)") public void logPointCut() {} @Around("logPointCut()") public Object around(ProceedingJoinPoint point) throws Throwable { long beginTime = System.currentTimeMillis(); Object result = null; Exception exception = null; try { // 执行业务逻辑 result = point.proceed(); return result; } catch (Exception e) { exception = e; throw e; // 异常必须抛出,否则事务无法回滚 } finally { long costTime = System.currentTimeMillis() - beginTime; // 无论成功失败,都记录日志 saveLog(point, costTime, exception); } } private void saveLog(JoinPoint joinPoint, long costTime, Exception exception) { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); SysAuditLog log = new SysAuditLog(); // 获取注解上的描述 AuditLog auditLog = method.getAnnotation(AuditLog.class); if (auditLog != null) { log.setModule(auditLog.module()); log.setOperation(auditLog.value()); } // 设置方法路径 String className = joinPoint.getTarget().getClass().getName(); String methodName = signature.getName(); log.setMethod(className + "." + methodName + "()"); // 设置请求参数 try { Object[] args = joinPoint.getArgs(); String params = objectMapper.writeValueAsString(args); log.setParams(DesensitizedUtil.desensitize(params)); // 关键:参数脱敏 } catch (Exception e) { log.setParams("参数解析异常"); } // 设置基础信息 log.setIp(IpUtils.getIpAddr()); log.setUsername(IpUtils.getCurrentUsername()); log.setCostTime(costTime); // 设置状态与错误信息 if (exception != null) { log.setStatus(0); log.setErrorMsg(exception.getMessage().length() > 500 ? exception.getMessage().substring(0, 500) : exception.getMessage()); } else { log.setStatus(1); } // 异步保存到数据库 asyncLogService.saveSysLog(log); } } ```

第五步:异步落库与线程池配置

档案管理系统审计不严格?教你五步搭建严格审计体系

为了避免审计日志写入数据库影响主业务性能(例如下载大文件时),必须使用异步方式保存日志。首先开启异步支持:

```java package com.example.archive.config; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableAsync; @Configuration @EnableAsync public class AsyncConfig { // 可以在这里自定义线程池,如不配置则使用Spring默认 } ```

接着创建Service接口和实现类。注意:这里必须使用@Async注解。

```java package com.example.archive.service; import com.baomidou.mybatisplus.extension.service.IService; import com.example.archive.entity.SysAuditLog; import org.springframework.scheduling.annotation.Async; public interface AsyncLogService extends IService { @Async void saveSysLog(SysAuditLog log); } ``` ```java package com.example.archive.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.example.archive.entity.SysAuditLog; import com.example.archive.mapper.SysAuditLogMapper; import com.example.archive.service.AsyncLogService; import org.springframework.stereotype.Service; @Service public class AsyncLogServiceImpl extends ServiceImpl implements AsyncLogService { @Override public void saveSysLog(SysAuditLog log) { // 直接调用MyBatis Plus保存 this.save(log); } } ```

对应的实体类SysAuditLog.java(字段需与数据库表对应):

```java package com.example.archive.entity; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import java.util.Date; @Data @TableName("sys_audit_log") public class SysAuditLog { @TableId(type = IdType.AUTO) private Long id; private String module; private String operation; private String method; private String params; private String ip; private String username; private Integer status; private String errorMsg; private Long costTime; private Date createTime; } ```

以及Mapper接口:

```java package com.example.archive.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.example.archive.entity.SysAuditLog; import org.apache.ibatis.annotations.Mapper; @Mapper public interface SysAuditLogMapper extends BaseMapper { } ```

第六步:在业务代码中接入审计

现在,你只需要在需要严格审计的Controller方法上加上@AuditLog注解即可。以下是一个档案管理Controller的示例:

```java package com.example.archive.controller; import com.example.archive.annotation.AuditLog; import org.springframework.web.bind.annotation.; import org.springframework.web.multipart.MultipartFile; @RestController @RequestMapping("/archive") public class ArchiveController { // 示例:删除档案操作,属于高风险操作,必须审计 @DeleteMapping("/delete/{id}") @AuditLog(value = "删除档案", module = "档案管理") public String deleteArchive(@PathVariable Long id) { // 业务逻辑:删除档案 if (id == 1) { throw new RuntimeException("模拟删除失败:权限不足"); } return "删除成功,ID:" + id; } // 示例:查询档案详情 @GetMapping("/detail/{id}") @AuditLog(value = "查看档案详情", module = "档案查询") public String getDetail(@PathVariable Long id) { return "档案详情内容..."; } } ```

第七步:验证审计效果

启动项目后,使用Postman或浏览器访问接口进行测试。

1. 测试正常操作:
访问 GET /archive/detail/100。检查数据库sys_audit_log表,应新增一条记录,status为1,params包含ID信息。

2. 测试异常操作:
访问 DELETE /archive/delete/1。根据代码逻辑,这会抛出异常。检查数据库,status应为0,error_msg应包含“权限不足”。

3. 验证敏感数据脱敏:
如果请求参数中包含手机号或身份证,检查数据库中的params字段,确认中间几位数字已被替换为号。

通过以上步骤,你已经为档案管理系统构建了一套零侵入、高性能且包含数据脱敏的严格审计体系。所有关键操作都有迹可循,满足合规要求。

微信咨询
电话联系
QQ客服
微信咨询一对一服务
服务热线: 028-8744 4417
QQ客服: 2305721818