test-backend/.claude/security.md

3.2 KiB
Raw Blame History

06 — 安全与日志规则

适用:所有后端开发任务。


安全规则

认证检查(不变式)

每个需要登录的接口必须通过 Shiro 认证。JeecgBoot 默认全局拦截。

// JeecgBoot 认证机制:
// 1. 所有 /sys/** /admin/** 默认需要登录
// 2. 白名单在 application.yml 配置
// 3. 权限注解:@RequiresPermissions / @RequiresRoles

// ✅ 接口加权限
@RequiresPermissions("user:add")
@PostMapping("/add")
public Result<?> add(...) { ... }

// ❌ 不要为了调试临时关认证
// ❌ 不要把需要认证的路径加到 shiro.excludeUrls

数据权限(租户隔离)

JeecgBoot 已通过 MyBatis-Plus 拦截器自动注入 tenant_id但如果手写 SQL 必须手动加

// ✅ 用 MyBatis-Plus 自动处理
taskService.list(new LambdaQueryWrapper<Task>().eq(Task::getStatus, "pending"));

// ❌ 手写 SQL 忘了加租户 → 租户A 看到租户B 的数据
@Select("SELECT * FROM task WHERE status = #{status}")

// ✅ 手写 SQL 正确写法
@Select("SELECT * FROM task WHERE status = #{status} AND tenant_id = #{tenantId}")

敏感信息保护

// ✅ 密码字段不返回
@Entity
public class SysUser {
    @JsonIgnore  // ← 关键
    private String password;
}

// ✅ 手机号脱敏返回VO 层处理)
userVO.setPhone(phone.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2"));

文件上传安全

// ✅ 必须校验JeecgBoot 已内置部分,确认开启)
- 文件类型白名单只允许 jpg/png/pdf/doc/xlsx
- 文件大小上限单文件  10MB
- 文件名处理 UUID 重命名防路径穿越
- 存储路径不暴露原始文件名给前端

接口防刷

// ✅ 敏感接口(登录/短信/支付)加限流
// JeecgBoot 使用 Sentinel 或自定义注解
@RateLimiter(count = 5, time = 60)  // 60秒内最多5次
@PostMapping("/send-sms")
public Result<?> sendSms(...) { ... }

日志规则

日志级别

级别 场景
ERROR 数据库连不上、第三方接口失败、业务异常需要人工介入
WARN 降级处理、重试成功、配置缺失但有默认值
INFO 用户登录、任务创建、状态变更、定时任务执行
DEBUG 开发调试用,不入生产

日志内容

// ✅ 关键操作
log.info("任务状态变更 taskId={} from={} to={} operator={}", taskId, oldStatus, newStatus, userId);

// ✅ 异常
log.error("调用第三方接口失败 url={}", url, e);  // 最后一个参数是异常对象

// ❌ 不要这样
log.info("进来了");
log.info("user=" + user.toString());  // 可能打印密码
System.out.println("debug: " + xxx);  // 永远不

禁止

原因
循环里打 INFO 日志爆炸
打印完整大对象 刷屏且可能泄露敏感数据
用 System.out.println 不入日志文件
用 e.printStackTrace() 不入日志框架

检查表

  • 新接口需要认证(不在白名单中)
  • 手写 SQL 加了租户隔离
  • 敏感字段不返回给前端
  • 文件上传有类型/大小校验
  • 关键操作有 INFO 日志
  • 异常有 ERROR 日志(带栈)
  • 无硬编码密码/密钥