Files
server/故障分析报告.md
2025-11-02 19:34:16 +08:00

4.0 KiB

Xiaozhi ESP32 Server - 用户未登录导致数据库插入失败故障分析

故障现象

  • 错误信息: java.sql.SQLIntegrityConstraintViolationException: Column 'userId' cannot be null
  • 发生时间: 2025-07-08 09:44:58 - 09:50:33
  • 影响接口: /api/config/add
  • 错误频率: 多次重复出现

问题分析

1. 根本原因

用户在未登录状态下访问了需要认证的接口 /api/config/add,导致:

  • CmsUtils.getUserId() 返回 null
  • 数据库插入时 userId 字段为 null
  • 违反数据库 NOT NULL 约束

2. 认证机制分析

当前认证流程:

  1. AuthenticationInterceptor 拦截所有请求
  2. 检查会话中是否有用户信息
  3. 如果没有,尝试从 Cookie 中获取用户名并自动登录
  4. 如果都失败,返回 401 未授权错误

配置问题:

// WebMvcConfig.java
registry.addInterceptor(authenticationInterceptor)
        .addPathPatterns("/**")
        .excludePathPatterns(
            "/api/user/login",
            "/api/user/register",
            "/api/device/ota",
            "/audio/**",
            "/uploads/**",
            "/avatar/**",
            "/ws/**"
        );

/api/config/** 路径需要认证,但拦截器似乎没有正确拦截。

3. 可能的原因

  1. Cookie 认证失败

    • Cookie 中的用户名存在,但数据库中找不到对应用户
    • Cookie 认证成功,但没有正确设置 userId
  2. 请求处理顺序问题

    • 拦截器执行顺序可能有问题
    • RequestContextHolder 的属性设置时机不对
  3. 前端问题

    • 前端可能在用户未登录时错误地调用了接口
    • 前端可能缓存了过期的认证信息

解决方案

1. 短期修复(已实施)

ConfigController 中添加显式的登录检查:

@PostMapping("/add")
@ResponseBody
public AjaxResult add(SysConfig config) {
    try {
        // 获取当前用户ID
        Integer userId = CmsUtils.getUserId();
        
        // 检查用户是否已登录
        if (userId == null) {
            return AjaxResult.error(HttpStatus.FORBIDDEN, "用户未登录,请先登录");
        }
        
        config.setUserId(userId);
        configService.add(config);
        return AjaxResult.success();
    } catch (Exception e) {
        logger.error(e.getMessage(), e);
        return AjaxResult.error();
    }
}

2. 长期改进建议

2.1 修复认证拦截器

确保拦截器在用户未登录时正确返回 401 错误,而不是让请求继续执行。

2.2 统一认证检查

创建一个基础控制器方法,统一处理用户认证检查:

protected Integer requireUserId() {
    Integer userId = CmsUtils.getUserId();
    if (userId == null) {
        throw new UnauthorizedException("用户未登录");
    }
    return userId;
}

2.3 全局异常处理

GlobalExceptionHandler 中添加对 UnauthorizedException 的处理。

2.4 前端改进

  • 在 API 调用前检查用户登录状态
  • 收到 401 错误时自动跳转到登录页面
  • 清理过期的认证信息

3. 需要检查的其他接口

以下接口也使用了 CmsUtils.getUserId(),需要添加类似的检查:

  • /api/role/add
  • /api/role/update
  • /api/role/query
  • /api/device/add
  • /api/device/update
  • /api/device/query
  • /api/agent/add
  • /api/template/add
  • /api/template/update
  • /api/template/query

监控建议

  1. 添加日志监控

    • 记录所有认证失败的请求
    • 监控 userId 为 null 的情况
  2. 添加告警

    • 当出现大量认证失败时发送告警
    • 监控数据库约束违反错误
  3. 定期审查

    • 审查所有需要用户认证的接口
    • 确保认证机制正常工作

总结

这是一个典型的认证机制失效导致的数据完整性问题。虽然已经通过在控制器层添加检查来临时解决,但根本问题是认证拦截器没有正确工作。建议尽快修复拦截器,并在所有需要用户认证的接口中添加统一的认证检查机制。