Listener 不适合处理 403,因其仅监听容器生命周期事件,无法捕获 HTTP 请求级状态码、修改响应内容、访问 MVC 上下文或区分 401/403。应使用 accessDeniedHandler、静态/模板错误页、自定义 BasicErrorController 或 @ControllerAdvice 处理。

403 页面自定义不推荐用 Exception Listener(比如 Servlet 的 ServletContextListener)来实现。它本质是监听容器生命周期事件(如启动、销毁),**无法捕获 HTTP 请求级别的状态码或权限拒绝逻辑**,更不能干预响应内容或跳转页面。
为什么 Listener 不适合处理 403?
Listener 关注的是 Web 容器层面的“环境变化”,不是请求处理链。Spring Security 拦截后返回 403,是在 Filter 链中完成的,此时请求早已离开 Listener 的作用范围。你无法在 Listener 里:
- 拿到当前请求的 HttpStatus 或错误原因
- 修改响应体、设置视图或重定向
- 访问 Spring MVC 的 Model、ModelAndView 或 Thymeleaf 上下文
- 区分是认证失败(401)还是授权不足(403)
真正有效的 403 自定义方式
针对 Spring Boot + Spring Security 场景,应优先使用以下方法:
-
配置 Spring Security 的 accessDeniedHandler:通过
HttpSecurity.authorizeHttpRequests()配合自定义AccessDeniedHandler,可精确控制 403 响应——返回 JSON、跳转 HTML 页面、或写入日志后重定向 -
静态或模板错误页(/error/403.html):放在
src/main/resources/templates/error/403.html(Thymeleaf)或src/main/resources/static/error/403.html(纯静态),Spring Boot 默认错误控制器会自动匹配并渲染 -
自定义 BasicErrorController 子类:继承并覆盖
errorHtml()方法,可注入用户信息、请求路径等动态数据到 403 页面 -
@ControllerAdvice + @ExceptionHandler 处理 AccessDeniedException:在全局异常处理器中捕获
AccessDeniedException,返回统一错误视图或响应体
一个最小可行的 accessDeniedHandler 示例
在 Security 配置类中:
http.exceptionHandling()
.accessDeniedHandler((request, response, accessDeniedException) -> {
request.setAttribute("status", 403);
request.setAttribute("message", "权限不足,请联系管理员");
RequestDispatcher dispatcher = request.getRequestDispatcher("/error/403");
dispatcher.forward(request, response);
});
再配一个 @RequestMapping("/error/403") 的 Controller 方法,或直接放好 templates/error/403.html 即可生效。

















