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

如何捕获Spring Boot中@PreAuthorize抛出的AccessDeniedException

问题:无法捕获AccessDeniedException自定义权限拒绝响应

问题背景

我想捕获AccessDeniedException,在响应里显示自定义的权限拒绝提示“Access denied.”,而不是默认的“Internal Server Error(s) while executing query”。但调试时,try/catch块抓不到filterChain.doFilter(request, response)抛出的这个异常,用推荐的@ExceptionHandler也没效果。

当前响应

{
    "errors": [
        {
            "message": "Internal Server Error(s) while executing query"
        }
    ]
}

期望响应

{
    "errors": [
        {
            "message": "Access denied."
        }
    ]
}

现有代码

JwtFilter 代码

@Component
public class JwtFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull FilterChain filterChain) {
        Optional<HttpServletRequest> optReq = Optional.of(request);
        String authToken = optReq
                .map(req -> req.getHeader("Authorization"))
                .filter(token -> !token.isEmpty())
                .map(token -> token.replace("Bearer ", ""))
                .orElse(null);

        if (authToken != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            //process token
        } else {
            try {
                filterChain.doFilter(request, response);
            } catch (AccessDeniedException | ServletException | IOException e) {
                String test = "test";
            }
        }
    }
}

使用@PreAuthorize的Mutation代码

@Component
@PreAuthorize("hasAnyRole('CREATOR','INFLUENCER','INFLUENCER_TEAMMATE')")
public class ProducerSharedMutations implements GraphQLMutationResolver {
       private final WidgetService widgetService;
       
       public ProducerSharedMutations(WidgetService widgetService) {
       this.widgetService = widgetService;
      }

      public Widget addWidget(WidgetInput widgetInput){
         return widgetService.add(widgetInput);
      }
}

解决方案

1. 捕获失败的原因

@PreAuthorize触发的AccessDeniedException是在Spring Security的方法拦截器阶段抛出的,你的JwtFilter处于过滤器链前端,此时方法层面的权限校验还未执行,所以filterChain.doFilter不会直接抛出该异常。另外,GraphQL会自动包裹原始异常,导致@ExceptionHandler无法匹配到目标异常类型。

2. 两种有效处理方式

方式一:自定义Spring Security的AccessDeniedHandler

创建自定义处理器替换默认逻辑,直接返回符合要求的响应:

@Component
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {
        response.setStatus(HttpServletResponse.SC_FORBIDDEN);
        response.setContentType("application/json");
        String jsonResponse = "{\"errors\": [{\"message\": \"Access denied.\"}]}";
        response.getWriter().write(jsonResponse);
    }
}

在Spring Security配置类中注册该处理器:

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    private final CustomAccessDeniedHandler customAccessDeniedHandler;

    public SecurityConfig(CustomAccessDeniedHandler customAccessDeniedHandler) {
        this.customAccessDeniedHandler = customAccessDeniedHandler;
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .anyRequest().authenticated()
            )
            .exceptionHandling(ex -> ex
                .accessDeniedHandler(customAccessDeniedHandler)
            );
        return http.build();
    }
}

方式二:GraphQL专属全局异常处理

针对GraphQL接口,实现自定义GraphQLErrorHandler拦截异常并替换消息:

@Component
public class CustomGraphQLErrorHandler implements GraphQLErrorHandler {
    @Override
    public List<GraphQLError> processErrors(List<GraphQLError> errors) {
        return errors.stream()
                .map(this::convertError)
                .collect(Collectors.toList());
    }

    private GraphQLError convertError(GraphQLError error) {
        if (error instanceof ExceptionWhileDataFetching) {
            ExceptionWhileDataFetching exceptionError = (ExceptionWhileDataFetching) error;
            Throwable cause = exceptionError.getException();
            if (cause instanceof AccessDeniedException) {
                return new GenericGraphQLError("Access denied.");
            }
        }
        return error;
    }
}

3. 注意事项

  • 确保JwtFilter在Spring Security过滤器链中配置顺序正确,避免提前拦截导致后续权限校验逻辑无法执行。
  • 若为GraphQL接口,优先使用方式二,更贴合GraphQL的响应格式规范。

内容的提问来源于stack exchange,提问作者Michael Bat

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 23:33:30