Spring Boot Security全局异常处理器捕获权限拒绝异常问题
问题场景
普通用户访问管理员页面时,期望通过@ControllerAdvice全局异常处理器捕获权限拒绝异常,但终端抛出org.springframework.security.authorization.AuthorizationDeniedException: Access Denied,且该异常未被全局处理器捕获,未使用JWT。
现有实现
- 全局异常处理器中针对
AccessDeniedException的处理:
@ExceptionHandler(AccessDeniedException.class) @ResponseStatus(HttpStatus.FORBIDDEN) public String handleAccessDenied(AccessDeniedException e, Model model) { model.addAttribute("errorMessage", e.getMessage()); return "error"; }
- 自定义
AccessDeniedHandler:
@Component public class CustomAccessDeniedHandler implements AccessDeniedHandler { @Override public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { throw accessDeniedException; } }
- SecurityConfig中配置异常处理:
.exceptionHandling(eh -> eh.accessDeniedHandler(customAccessDeniedHandler))
核心问题与解决方案
1. 异常类型不匹配
你当前捕获的AccessDeniedException是Servlet规范定义的异常,而终端抛出的AuthorizationDeniedException是Spring Security 5.6+新增的授权决策异常,二者无继承关系,因此全局处理器未触发。
解决:添加AuthorizationDeniedException的处理方法
在GlobalExceptionHandler中新增对应处理逻辑,覆盖两种权限异常场景:
@ExceptionHandler(AuthorizationDeniedException.class) @ResponseStatus(HttpStatus.FORBIDDEN) public String handleAuthorizationDenied(AuthorizationDeniedException e, Model model) { model.addAttribute("errorMessage", "权限不足,无法访问该页面"); return "error"; }
2. 过滤器链异常未传递到DispatcherServlet
Spring Security的过滤器执行在DispatcherServlet之前,直接抛出的异常无法被@ControllerAdvice捕获,需要将异常转发到DispatcherServlet处理。
解决:调整CustomAccessDeniedHandler逻辑
修改自定义处理器,将异常转发到错误请求路径,让DispatcherServlet接管:
@Component public class CustomAccessDeniedHandler implements AccessDeniedHandler { @Override public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { // 将异常存入request属性,转发到error路径 request.setAttribute("javax.servlet.error.exception", accessDeniedException); request.getRequestDispatcher("/error").forward(request, response); } }
同时,针对AuthorizationDeniedException,需要在SecurityConfig中补充授权异常的处理(因为该异常不由AccessDeniedHandler处理),可以通过调整异常处理配置:
.exceptionHandling(eh -> eh .accessDeniedHandler(customAccessDeniedHandler) .authenticationEntryPoint((request, response, ex) -> { // 处理未认证场景,同样转发到error页面 request.setAttribute("javax.servlet.error.exception", ex); request.getRequestDispatcher("/error").forward(request, response); }) )
3. 简化配置(可选)
如果无需自定义AccessDeniedHandler,可以直接移除相关配置,依赖Spring Security默认的异常处理机制,同时确保全局处理器包含两种异常的处理方法即可。
内容的提问来源于stack exchange,提问作者David

