一、需求分析 1.日志记录目的: -审计追踪:记录用户所有关键操作,便于问题排查和合规性检查 -数据分析:通过操作日志分析用户行为模式 -安全监控:检测异常操作行为 2.需记录内容: -操作时间 -操作用户 -操作类型(增删改查等) -操作对象(如商品ID、订单号等
一、需求分析
1. 日志记录目的:
- 审计追踪:记录用户所有关键操作,便于问题排查和合规性检查
- 数据分析:通过操作日志分析用户行为模式
- 安全监控:检测异常操作行为
2. 需记录内容:
- 操作时间
- 操作用户
- 操作类型(增删改查等)
- 操作对象(如商品ID、订单号等)
- 操作结果(成功/失败)
- 客户端信息(IP、设备类型等)
- 操作详情(可选,如修改前后的值)
二、技术方案设计
1. 日志存储方案
方案一:数据库存储
- 优点:结构化查询方便,可与业务数据关联
- 缺点:高并发写入可能影响性能
方案二:文件存储+ELK
- 优点:高性能写入,适合大规模日志
- 缺点:需要额外维护ELK栈
最终选择:混合方案
- 核心业务操作日志存入MySQL(便于审计)
- 非关键操作日志写入文件,由Filebeat收集到ELK
2. 日志级别设计
| 级别 | 描述 | 示例 |
|------|------|------|
| INFO | 常规操作记录 | 用户登录、商品查询 |
| WARN | 可疑但未失败的操作 | 频繁错误密码尝试 |
| ERROR | 操作失败记录 | 创建订单失败 |
| CRITICAL | 严重系统问题 | 数据库连接中断 |
3. 关键表设计(MySQL)
```sql
CREATE TABLE `operation_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` bigint(20) DEFAULT NULL COMMENT 操作用户ID,
`username` varchar(50) DEFAULT NULL COMMENT 用户名,
`operation_type` varchar(20) NOT NULL COMMENT 操作类型(CREATE/UPDATE/DELETE/VIEW),
`module` varchar(50) NOT NULL COMMENT 操作模块(商品/订单/库存等),
`resource_id` varchar(64) DEFAULT NULL COMMENT 操作资源ID,
`resource_type` varchar(50) DEFAULT NULL COMMENT 资源类型,
`ip_address` varchar(50) DEFAULT NULL COMMENT 操作IP,
`user_agent` varchar(500) DEFAULT NULL COMMENT 用户代理,
`status` tinyint(1) DEFAULT 1 COMMENT 状态(1成功 0失败),
`result_data` text COMMENT 操作结果数据(JSON),
`request_params` text COMMENT 请求参数(JSON),
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间,
`cost_time` int(11) DEFAULT NULL COMMENT 耗时(毫秒),
PRIMARY KEY (`id`),
KEY `idx_user` (`user_id`),
KEY `idx_time` (`create_time`),
KEY `idx_module` (`module`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT=用户操作日志表;
```
三、开发实现
1. Spring AOP实现日志记录
```java
@Aspect
@Component
public class OperationLogAspect {
@Autowired
private OperationLogService logService;
@Autowired
private HttpServletRequest request;
// 定义切点:标注了@OperationLog的方法
@Pointcut("@annotation(com.kuailv.annotation.OperationLog)")
public void logPointCut() {}
@Around("logPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
long beginTime = System.currentTimeMillis();
// 执行方法
Object result = point.proceed();
// 执行时长(毫秒)
long time = System.currentTimeMillis() - beginTime;
// 保存日志
saveLog(point, time, null);
return result;
}
@AfterThrowing(pointcut = "logPointCut()", throwing = "e")
public void afterThrowing(JoinPoint point, Exception e) {
long time = System.currentTimeMillis() - System.currentTimeMillis(); // 实际应记录开始时间
saveLog((ProceedingJoinPoint)point, time, e);
}
private void saveLog(ProceedingJoinPoint joinPoint, long time, Exception e) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
OperationLog operationLog = new OperationLog();
OperationLog annotation = method.getAnnotation(OperationLog.class);
// 设置注解上的信息
if(annotation != null){
operationLog.setModule(annotation.module());
operationLog.setOperationType(annotation.operationType().name());
// 其他注解属性...
}
// 从切面上下文中获取方法参数
Object[] args = joinPoint.getArgs();
try {
// 处理参数,记录关键信息(避免记录敏感信息)
String params = JsonUtils.toJson(args);
operationLog.setRequestParams(params);
} catch (Exception ex) {
// 参数序列化失败处理
}
// 获取请求信息
String ip = IpUtils.getIpAddr(request);
operationLog.setIpAddress(ip);
operationLog.setUserAgent(request.getHeader("User-Agent"));
// 获取用户信息
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if(authentication != null && authentication.getPrincipal() instanceof UserDetails){
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
operationLog.setUserId(((User)userDetails).getId());
operationLog.setUsername(userDetails.getUsername());
}
// 异常信息
if(e != null){
operationLog.setStatus(0); // 失败
operationLog.setResultData(e.getMessage());
} else {
operationLog.setStatus(1); // 成功
// 可记录返回结果的关键信息
}
operationLog.setCostTime((int)time);
operationLog.setCreateTime(new Date());
// 异步保存日志
logService.saveAsync(operationLog);
}
}
```
2. 日志注解定义
```java
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OperationLog {
/
* 操作模块
*/
String module() default "";
/
* 操作类型
*/
OperationType operationType() default OperationType.VIEW;
/
* 是否保存请求参数
*/
boolean saveParams() default true;
/
* 是否保存结果数据
*/
boolean saveResult() default false;
/
* 操作描述
*/
String desc() default "";
}
public enum OperationType {
CREATE, UPDATE, DELETE, VIEW, OTHER
}
```
3. 异步日志服务
```java
@Service
public class OperationLogServiceImpl implements OperationLogService {
@Autowired
private OperationLogMapper logMapper;
@Async
@Override
public void saveAsync(OperationLog log) {
try {
logMapper.insert(log);
} catch (Exception e) {
// 失败处理,可写入文件或发送到消息队列重试
log.error("保存操作日志失败", e);
}
}
}
```
四、高级功能实现
1. 日志脱敏处理
```java
public class LogDesensitizationUtil {
public static String desensitize(String content) {
if(StringUtils.isBlank(content)){
return content;
}
// 手机号脱敏
content = content.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1$2");
// 身份证号脱敏
content = content.replaceAll("(\\d{4})\\d{10}(\\w{4})", "$1$2");
// 邮箱脱敏
content = content.replaceAll("(\\w?)(\\w+)(\\w)(@\\w+\\.[a-z]+(\\.[a-z]+)?)", "$1$3$4");
return content;
}
}
```
2. 日志查询接口
```java
@RestController
@RequestMapping("/api/logs")
public class LogController {
@Autowired
private OperationLogService logService;
@GetMapping
public Result
> queryLogs(
@RequestParam(required = false) String username,
@RequestParam(required = false) String module,
@RequestParam(required = false) Date startTime,
@RequestParam(required = false) Date endTime,
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "20") Integer pageSize) {
PageHelper.startPage(pageNum, pageSize);
List logs = logService.queryLogs(username, module, startTime, endTime);
PageInfo pageInfo = new PageInfo<>(logs);
PageData pageData = new PageData<>();
pageData.setTotal(pageInfo.getTotal());
pageData.setList(BeanCopyUtils.copyList(logs, OperationLogVO.class));
return Result.success(pageData);
}
}
```
五、部署与监控
1. 日志轮转配置:
- 对于文件日志,配置logrotate每日轮转
- 设置保留周期(如30天)
2. 监控告警:
- 监控日志写入失败率
- 对ERROR级别日志设置告警阈值
- 监控异常操作模式(如短时间内大量失败登录)
3. 性能优化:
- 批量插入日志(每100条或每5秒)
- 对高频操作采用采样记录(如商品浏览日志)
六、测试用例
1. 正常操作测试:
- 创建商品并验证日志记录
- 修改订单状态并验证日志
2. 异常操作测试:
- 无权限操作验证日志
- 系统异常时日志记录
3. 性能测试:
- 高并发下日志写入性能
- 日志查询响应时间
七、后续优化方向
1. 引入日志压缩减少存储空间
2. 实现日志自动归档和清理策略
3. 增加日志行为分析功能,识别潜在安全威胁
4. 对关键操作实现双因素认证并记录
通过以上方案,快驴生鲜系统实现了全面的用户操作日志记录功能,既满足了审计合规要求,也为系统运维和安全分析提供了有力支持。