# 后端JAVA代码 Code Review 检查项 标签语义: - 【停用】:本条不检查,扫描时跳过 - 【待改造】:本条仍然要检查,按目标态标准执行;发现的存量不合规项不报违规,归入清单管理 ## 一、架构与设计类 1. **分层规范(Controller / Service / DAO 职责边界)** - Controller 只做参数接收校验、调 Service、组装响应,禁止写业务逻辑、禁止直接调 Mapper/DAO - Service 承担业务逻辑与事务边界,不出现 HttpServletRequest 等 Web 层对象 - DAO/Mapper 只做数据存取,不写业务判断;禁止 Service 之间循环依赖 2. **面客端与管理端接口分离** - 面客端(网站 / APP / 小程序)和管理端(WEB / APP)的接口不得混在同一个 Controller 里,必须分开建 Controller。 - URL 规范: - 【待改造】管理端接口路径规范:/api/<服务>/<路径> - 【待改造】面客端接口路径规范: /c-api/<服务>/client/<路径>,即路径必须含 /client/ 段 - 接口归属判定:以 Controller 映射路径是否含 /client/ 段为准,**含 /client/ 段为面客端接口,无 /client/ 段一律视为管理端接口**;与前端源码(面客端 / 管理端分别提供)做交叉验证,面客端前端调用了但没有 /client/ 段的,列入改造清单;有 /client/ 段但没有被面客端前端调用的,列入清理清单。 - 两端走各自的认证鉴权链和过滤器配置,互不复用;管理端接口不得挂在面客端路由下,反之亦然 - 管理端网关:/api - 面客端网关:/c-api,不带 /client/ 段的路由一律拒绝(须在路由匹配前以 GlobalFilter 拦截) - 接口归属须与用户提供并标注的前端源码双向交叉验证:同一 Controller 被两端前端共同调用即违规;前端调用了但后端找不到对应 Controller 的路径,按违规报告;接口既未被管理端前端调用、也未被面客端前端调用的,列为未归属接口报告,不得静默跳过(第 7 条的越权检查依赖本条的归类结果) 3. **接口设计** - RESTful 规范:所有接口统一使用 POST,参数一律通过 @RequestBody 从请求 body 中接收;禁止使用 GET / PUT / DELETE,禁止用 @RequestParam / @PathVariable / 表单形式接收参数。确有特殊情况(如第三方回调、文件下载等)必须使用其他方式的,须在代码注释中明确注明原因,未注明原因的按违规报告;统一返回格式 { code, message, data } - 写接口需幂等:重复调用不产生副作用 - 禁止开启 Swagger / OpenAPI 文档端点,不管什么环境(项目未使用,发现依赖或配置开启即报告) 4. **第三方调用** - 调用外部服务必须设置超时(connect + read) - 必须有降级 / 重试策略(熔断器如 Sentinel / Resilience4j);重试设最大次数和退避间隔 - 关键调用必须记录请求和响应日志(脱敏后) ## 二、安全类 5. **【停用】CVE 漏洞扫描** - 检查 pom.xml 中写死版本号的三方依赖,比对 NVD / Snyk / dependency-check 是否落在已知漏洞影响区间内 - 同一依赖出现多个版本并存的,报告中列出各模块使用的版本,建议收敛到父 pom 统一管理;扫描阶段只标记不修复 - 命中漏洞的依赖:报告中列出所用版本、漏洞编号(CVE ID)、官方修复版本号;扫描阶段只标记不修复 6. **SQL 注入** - mapper XML 和注解 SQL(@Select 等)中,凡是接收外部输入的地方一律用 #{} 预编译,禁止 ${} 拼接和字符串拼接 SQL - ORDER BY 排序字段:列名不能用 #{} 预编译,服务端必须用白名单映射(前端传别名,服务端换真实列名),方向只允许 asc / desc - 分页 limit:每页条数、页码等数值用 #{} 预编译传参,禁止 ${} 拼接(如 limit ${offset});每页条数设上限(最大 1000 条)防止一次拉全表 - MyBatis-Plus Wrapper 的 .last() / .apply() 中不允许拼接用户输入;IN 子句参数数量设置上限(最大 10个) 7. **没有增加权限标识(含越权)** - 每个接口都必须声明鉴权方式:方法上加权限注解(`@PreAuthorize` / `@RequiresPermissions` / 自定义注解),或在 SecurityConfig 中配置路径规则;没声明的就是缺权限标识 - 管理端接口(无 /client/ 段)权限注解必须全覆盖,写接口(新增/修改/删除)和接收身份参数的接口必须全覆盖。 - 区分接口是面客端还是管理端,再定用户 ID 的来源: - 面客端接口(面客端网站 / APP / 小程序):查当前用户数据时,用户 ID 必须从登录 token 中获取,禁止直接接收前端传入的 userId 参数,防止横向越权 - 管理端接口(管理端 WEB / APP):可以按传入的 userId 查询任意用户数据,但必须挂权限标识,登录 ≠ 有权限 - 接口归属面客端还是管理端,按第 2 条的归类规则判定;同一接口被两端前端共同调用的,越权按"从严"(面客端)规则检查 - 根据 ID 查询/修改/删除资源时,必须校验资源归属(用户 A 不能操作用户 B 的数据);普通用户不能调用管理员接口(垂直越权) - 匿名放行的白名单逐条注明原因,写操作一律不允许匿名访问 8. **认证与会话安全(暴力破解、弱密码、短信轰炸防护)** - 防暴力破解:同一账号 / IP 连续登录失败 N 次(如 5 次)后锁定或要求验证码,失败计数要落库并可告警 - 密码强度:长度 ≥ 8 位且至少含 3 类字符(大小写 / 数字 / 符号),拒绝常见弱密码(123456、admin123)和与账号相同的密码 - 密码存储用 BCrypt 等加盐慢哈希,禁止 MD5 / SHA / 明文 - 短信轰炸防护:验证码同一手机号 60 秒 1 条,发送前必须先过图形验证码等人机校验 - 会话与 token:必须有过期时间,退出登录、修改密码后旧 token 要立即失效 9. **XSS / CSRF** - XSS 过滤链必须启用(XssFilter 默认关闭,需配置 renren.xss.enabled=true 才生效);富文本内容入库前必须清洁 - Cookie 会话的服务必须有 CSRF Token;Authorization 头传 token 的接口天然免疫,无需检查 - 响应头正确设置 Content-Type,避免浏览器自动嗅探 10. **敏感信息泄露** - 日志中禁止出现密码、Token、身份证号、手机号、银行卡号;如需记录必须脱敏(如 138****1234) - 【停用】配置文件禁止明文存储密码/密钥,使用加密配置或密钥管理服务 - 异常堆栈不得直接返回给前端,统一包装为标准错误响应 ## 三、代码质量类 11. **魔法变量** - 一个变量在生命周期内被不同代码块反复赋值、复用作不同用途(如 result / flag / temp 先后装多种东西),含义随位置漂移,极易误读误改 - 不同用途必须拆成各自独立命名的变量,做到见名知义 - 缩小变量作用域:就近声明、尽量一次赋值,禁止在大方法里跨代码块共享一个变量 12. **非空验证** - 接口入参用 DTO + @Validated + JSR-303 注解校验,禁止用 Map 裸接参数 - 查询结果(getById、getOne、list.get(0) 等)必须先判空再取值;链式调用(user.getAddress().getCity())注意中间环节判空 - 外部调用(RPC / HTTP / MQ)的返回值使用前必须判空;集合用 CollectionUtils.isEmpty() 判空,Map 取值用 getOrDefault 13. **异常处理** - 禁止空 catch 块、禁止 e.printStackTrace(),捕获后至少 log.error 带完整堆栈 - 统一走 @ControllerAdvice + @ExceptionHandler 全局捕获;自定义业务异常继承 RuntimeException 并携带错误码 - 不要用异常做流程控制;finally 块中禁止 return(会覆盖 try 中的返回值) 14. **命名与方法设计** - 类名大驼峰、方法/变量小驼峰、常量全大写下划线;布尔以 is/has/can 开头 - 单个方法不超过 200 行、参数不超过 20 个、嵌套不超过 5 层,用早返回优化 - 【停用】方法名体现行为:get 查询 / save 新增 / update 修改 / remove 删除 / delete 删除 ## 四、性能类 15. **数据库查询** - 禁止循环内查库(N+1 问题):改批量查询或 JOIN;批量插入用 INSERT VALUES (...),(...) 或 batch 模式 - 禁止 SELECT *;大结果集必须分页;单表查询必须命中索引(EXPLAIN 确认执行计划) - LIKE '%keyword%' 左模糊无法走索引,考虑全文检索(ES) - @Transactional 范围尽量小,事务中不调外部接口 16. **【停用】缓存** - 高频读、低频写的数据加缓存 - 缓存必须设置过期时间 - 防缓存穿透(不存在的 key)、雪崩(同时过期)、击穿(热点 key);修改数据后必须同步更新/删除缓存 17. **【停用】并发与线程安全** - 线程池必须自定义配置,禁止 Executors.newFixedThreadPool() 无界队列;异步任务必须设置超时时间 - SimpleDateFormat 非线程安全,用 DateTimeFormatter;Controller(单例)禁止用实例变量存请求状态 - 余额扣减、库存扣减必须用乐观锁或悲观锁防超卖;定时任务多实例部署时加分布式锁防重复执行 18. **资源管理** - IO 流、数据库连接必须用 try-with-resources 或 finally 关闭 - 文件上传限制大小;下载文件用流式传输,避免一次性加载到内存 ## 五、日志与可观测性 19. **日志规范** - 使用 SLF4J + Logback / Log4j2,禁止 System.out.println;禁止在循环中打日志 - 级别正确:ERROR 系统异常需人工介入 / WARN 可恢复问题 / INFO 关键业务节点 / DEBUG 生产环境关闭 - 日志必须包含上下文(traceId / userId / 关键参数);异常日志打完整堆栈 log.error("msg", e) 20. **监控埋点(慢 SQL、线程池、GC)** - 慢 SQL:数据源开启慢 SQL 统计(如 Druid slowSqlMillis / p6spy),超阈值(如 500ms)记日志并告警 - 【停用】线程池:自建线程池必须用 ThreadFactory 命名、暴露活跃线程数 / 队列积压指标,禁用 Executors 快捷方法 - 【停用】GC 与链路:启动参数开启 GC 日志,接入 actuator / micrometer,核心接口有耗时打点(成功率、耗时、QPS) 21. **【待改造】关键操作日志埋点(增加、修改、删除)** - 对数据库的新增、修改、删除操作,必须记录操作日志:操作人、操作时间、操作内容(含操作前后数据快照)