核心是用@controlleradvice统一拦截sqlexception及其子类,响应体仅返回模糊提示如“操作失败,请稍后重试”,禁用e.getmessage()等敏感信息;日志记录sqlstate和errorcode,不打原始消息,堆栈深度限制为0,并从源头通过preparedstatement、白名单校验、参数预校验和权限收紧防范泄露。

核心思路是:不让原始 SQLException 消息穿透到响应体,同时确保内部可定位问题、日志不带敏感上下文。
用 @ControllerAdvice 拦截所有数据库异常
在 Spring Boot 中,统一捕获 SQLException 及其子类,避免分散 try-catch:
- 定义全局异常处理器类,添加 @ControllerAdvice 注解
- 用 @ExceptionHandler(SQLException.class) 精准拦截数据库层异常
- 响应体只返回模糊提示,如 "操作失败,请稍后重试",HTTP 状态码保持 500 或自定义错误码(如 503)
- 绝对禁止把
e.getMessage()、e.toString()或e.getStackTrace()拼进 ResponseEntity.body()
日志中只记标准化错误码,不记原始消息
SQLException 的 getMessage() 常含表名、字段名、驱动版本等攻击情报,但 getSQLState() 和 getErrorCode() 是标准化的、无上下文的标识:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 记录日志时写成:log.error("DB operation failed", e) —— 这样堆栈进日志,但消息体为空
- 额外提取并打点:log.error("SQLState: {}, ErrorCode: {}", e.getSQLState(), e.getErrorCode())
- 配置 Logback 的 PatternLayout 使用
%ex{0},限制堆栈深度为 0 层,避免完整堆栈落入可查日志
前置切断注入路径,让 SQLException 尽量不发生
异常处理是兜底,真正防泄露要从源头减少 SQLException 触发机会:
- 所有含用户输入的 SQL 必须用 PreparedStatement,禁用字符串拼接(包括
"SELECT * FROM " + table) - 动态表名、列名、ORDER BY 字段等,必须走白名单校验(如
List.of("name", "email").contains(sortField)) - ID 类参数在进入 DAO 前就转 Long/Integer,捕获 NumberFormatException 并拒绝,不留给 JDBC 解析恶意字符串
- 分页参数 pageSize 强制上限(如
Math.min(pageSize, 100)),防大值触发数据库超时或语法异常
数据库账号权限与连接配置收紧
即使异常发生,也要让泄露信息的价值降到最低:
- 应用专属数据库账号仅授予 SELECT/INSERT/UPDATE 权限,禁用 DROP、SHOW TABLES、LOAD_FILE、EXECUTE 等高危操作
- JDBC URL 中启用安全参数,如 useSSL=true&allowPublicKeyRetrieval=false&dumpSql=false
- 避免在日志中打印完整 SQL 语句(尤其开启
spring.jpa.show-sql=true时)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










