You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Security自定义AccessDeniedHandler未触发问题排查

问题根因

你的判断完全正确:标注@Provider的ExceptionMH是JAX-RS规范(Jersey/Resteasy等框架)的全局异常处理器,执行优先级高于Spring Security配置的AccessDeniedHandler。
你开启了global-method-security的pre-post-annotations,@PreAuthorize等注解做方法级权限校验时抛出的AccessDeniedException,会先被JAX-RS的异常捕获机制拦截,直接生成响应返回,根本不会传递到Spring Security Filter链的异常处理节点,自然触发不了自定义的AccessDeniedHandler。
另外你写的CustomAccessDeniedHandler本身也有缺陷:就算触发,只打印日志不写入响应、不做跳转/转发,会导致请求直接挂住。

修复方案

推荐选第一种方案,改动最小、最适配你当前的混合技术栈:

方案1:将访问日志逻辑迁移到JAX-RS异常处理器中

既然AccessDeniedException已经被ExceptionMH捕获,直接在对应分支加日志逻辑即可,不需要强行调整过滤器优先级:

  1. 给ExceptionMH注入请求上下文,获取当前请求URI:
@Context
private HttpServletRequest request;
  1. 修改AccessDeniedException的处理分支,补充日志逻辑,同时修正状态码错误:
} else if (exception.getClass() == AccessDeniedException.class) {
  // 补充你需要的URI记录逻辑
  Authentication auth = SecurityContextHolder.getContext().getAuthentication();
  if (auth != null) {
    logger.warn("User: " + auth.getName()
        + " attempted to access the protected URL: "
        + request.getRequestURI());
  }
  this.exceptionError(exception);
  // 注意:无权限访问对应HTTP状态码是403,你之前写的401是未认证语义,不符合规范
  return Response.status(403).entity("{ \"error\": \"" + exception.getMessage() + "\" }").build();
}
  1. 删掉Spring Security配置中冗余的<access-denied-handler ref="customAccessDeniedHandler"/>和对应Bean定义即可。
    额外冗余代码提示:ExceptionMH不需要继承Throwable,ExceptionMapper接口不要求实现类是异常类型,这个继承会增加不必要的异常栈开销,可以直接删掉。

方案2:强制调整异常处理优先级(不推荐)

如果一定要走AccessDeniedHandler逻辑,需要做两层改动:

  • 调整Spring Security过滤器链的加载顺序,把ExceptionTranslationFilter放到JAX-RS过滤器之前执行
  • 额外配置方法级安全的异常透传规则,让@PreAuthorize等注解抛出的AccessDeniedException不被JAX-RS提前拦截,透传到外层Filter链处理
    这个方案改动链路长,很容易导致其他异常的拦截逻辑失效,除非有特殊需求否则不建议使用。

如果你要保留AccessDeniedHandler的写法,必须补全响应逻辑

如果后续场景需要用到AccessDeniedHandler,一定要在日志打印后补全响应输出,避免请求挂起:

@Override
public void handle(
    HttpServletRequest request,
    HttpServletResponse response,
    AccessDeniedException exc) throws IOException, ServletException {

  Authentication auth = SecurityContextHolder.getContext().getAuthentication();
  if (auth != null) {
    logger.warn("User: " + auth.getName()
        + " attempted to access the protected URL: "
        + request.getRequestURI());
  }
  // 补全响应逻辑
  response.setStatus(HttpServletResponse.SC_FORBIDDEN);
  response.setContentType("application/json;charset=UTF-8");
  response.getWriter().write("{ \"error\": \"" + exc.getMessage() + "\" }");
}

内容的提问来源于stack exchange,提问作者Paul Serre

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 01:33:19