# 异常、日志与安全 ## 异常 - **必须**:异常表达失败语义,不用异常控制正常分支。 - **必须**:不要直接抛出宽泛的 `Exception`、`RuntimeException` 或 `Throwable`;使用标准的精确异常或稳定的领域异常。 - **必须**:不得空 `catch`、只打印堆栈或吞掉异常。转换异常时保留 cause,并补充不含敏感数据的上下文。 - **必须**:同一层通常选择“处理并记录”或“向上抛出”,避免每层重复记录同一异常。 - **必须**:可通过前置检查避免的错误应先校验;并发竞态场景仍以原子操作结果为准。 - **必须**:资源使用 try-with-resources;`finally` 不得用 `return` 覆盖原返回值或异常。 - **必须**:捕获范围保持最小,不用一个大 `try` 模糊具体失败点。 - **推荐**:对外错误模型稳定、可追踪且不泄漏内部栈、SQL、文件路径或依赖细节。 ## 日志 - **必须**:使用项目日志门面,不使用 `System.out`、`System.err` 或 `printStackTrace`。 - **必须**:参数化记录日志,不做无必要的字符串拼接;异常对象作为日志框架支持的异常参数传入。 - **必须**:日志级别符合可操作性:预期业务拒绝通常不是 error,系统不可恢复失败不能只写 debug。 - **必须**:禁止记录密码、令牌、密钥、完整证件号、银行卡、Cookie、会话或未脱敏请求体。 - **推荐**:记录稳定的事件、结果、耗时、非敏感标识和 trace/correlation id;避免在高频循环中刷屏。 - **必须**:日志不得改变业务行为;日志表达式不得触发远程调用、延迟加载或明显昂贵计算。 - **推荐**:项目同时使用 SLF4J 和 Lombok 时采用 `@Slf4j`,否则遵循项目统一的日志门面与声明方式。 - **必须**:`error` 仅用于由当前层最终处理、需要人工介入的系统失败,并包含异常堆栈和已脱敏的定位上下文。 - **推荐**:`warn` 用于异常但可恢复、仍需要关注的状态。 - **推荐**:预期业务拒绝通常使用 `info` 或不记录,避免制造告警噪声。 - **必须**:同一异常只在负责最终处理的层记录一次;转换异常时保留原始 cause,不在中间层重复打印堆栈。 ## 输入与权限 - **必须**:所有外部输入在信任边界验证类型、长度、范围、格式、集合规模和允许值;前端校验不能替代服务端校验。 - **必须**:认证后仍要做功能级和对象级授权,不能只检查“是否登录”。 - **必须**:SQL 使用参数绑定;排序字段、表名、列名等不可绑定的元数据必须采用服务端白名单。 - **必须**:输出到 HTML、脚本、URL、日志或命令时按目标上下文编码;不要自行拼接转义规则。 - **必须**:状态变更接口按框架能力启用 CSRF、防重放、幂等或频控措施。 - **必须**:文件路径规范化并限制在允许根目录;URL 请求限制协议、主机、重定向和内网地址以防 SSRF。 - **必须**:禁止反序列化不可信 Java 原生对象流;JSON 多态、表达式、脚本和正则能力采用最小白名单。 - **必须**:凭据来自受控配置或密钥服务,不写入源码、测试、日志和错误响应。 - **推荐**:使用成熟安全库和框架默认防护,不自行实现密码学、会话、签名或随机令牌算法。 ## 事务与一致性 - **必须**:事务边界与业务原子性一致,避免在长事务中执行远程调用或不可控 I/O。 - **必须**:明确异常类型是否触发回滚;框架代理、自调用和异步边界可能使事务注解失效。 - **必须**:事务方法捕获异常后必须重新抛出、显式标记回滚或完成可靠补偿。 - **必须**:重试仅用于可重试失败,并要求操作幂等、退避、次数上限和可观测性。 - **推荐**:跨系统一致性采用项目既有 outbox、事件或补偿机制,不临时发明“先写库再发消息”的脆弱流程。