应屏蔽敏感堆栈信息,只对外返回错误码和简明业务提示;自定义异常需重写getmessage()、避免super.tostring(),日志中脱敏记录原始异常,全局处理器统一响应code/message/requestid。

在自定义异常中屏蔽敏感的系统堆栈信息,核心是避免将原始异常的完整堆栈(尤其是类路径、文件名、行号、内部方法调用链等)直接暴露给外部,尤其在 Web 接口或日志中。关键做法是:不直接抛出原始异常、不调用 printStackTrace()、不把 Throwable.getStackTrace() 暴露给前端或日志输出,而是构造干净、可控的错误响应。
只保留必要错误码和业务提示
自定义异常应聚焦于“用户能理解、系统能分类处理”的信息,而非技术细节:
- 定义统一错误码(如
USER_NOT_FOUND: 1002),不依赖堆栈判断类型 - 提供简明中文提示(如
"用户不存在,请检查账号"),不含路径、变量值、数据库名等 - 在构造异常时,不传入原始异常作为 cause(除非内部日志需要),或明确分离“对外消息”与“对内日志”
重写 toString() 和 getMessage()
防止框架(如 Spring)默认序列化时泄露堆栈:
- 在自定义异常类中,覆盖
getMessage(),仅返回业务消息,不拼接getCause().toString() - 避免重写
toString()时调用super.toString()(它会包含类名+message+cause) - 示例:错误写法:
return super.toString() + " | " + userInput;;正确写法:return "订单提交失败";
日志记录与异常传播要分开处理
安全不是隐藏所有信息,而是分级披露:
- 对外响应(如 REST API)只返回错误码 + 精炼提示,绝不包含 stackTraceElement 数组内容
- 内部日志可记录完整原始异常(含堆栈),但需脱敏:过滤密码字段、token、SQL 片段、绝对路径等
- 使用 MDC 或结构化日志(如 JSON),确保敏感字段不被意外打印;例如用
log.error("Order process failed", ex);是安全的,但log.error(ex.getMessage(), ex);可能泄露 cause 的 message
全局异常处理器中主动截断堆栈
以 Spring Boot 为例,在 @ControllerAdvice 中统一拦截:
- 对所有异常,响应体只含
code、message、requestId(用于追溯) - 若需调试,通过
requestId查后台日志,而不是把堆栈塞进 HTTP 响应 - 禁用开发环境以外的
server.error.include-stacktrace=always(Spring Boot 默认为on_trace_param,仍需注意)










