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

Spring Boot全局异常处理器无法捕获SAMLUserDetailsServiceImpl中的异常

问题分析

@ControllerAdvice 注解的全局异常处理器只能捕获Spring MVC控制器调用链中抛出的异常,而SAMLUserDetailsServiceImpl.loadUserBySAML方法是在Spring Security的SAML认证过滤器链中执行的,不在MVC控制器的处理流程内,因此异常无法被全局处理器拦截,最终触发Spring默认的白标错误页面。

解决方案

方案1:自定义SAML认证失败处理器

创建专属处理器处理SAML认证流程中的异常,返回自定义格式响应:

@Component
public class CustomSAMLAuthenticationFailureHandler implements SAMLAuthenticationFailureHandler {

    private final ObjectMapper objectMapper;

    public CustomSAMLAuthenticationFailureHandler(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        Throwable cause = exception.getCause();
        HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
        String message = "认证失败";

        if (cause instanceof IllegalArgumentException) {
            status = HttpStatus.BAD_REQUEST;
            message = cause.getMessage();
        } else if (cause instanceof UsernameNotFoundException) {
            status = HttpStatus.NOT_FOUND;
            message = "用户不存在";
        }

        response.setStatus(status.value());
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        ApiException apiException = new ApiException(message, status, ZonedDateTime.now(ZoneId.of("Z")));
        objectMapper.writeValue(response.getWriter(), apiException);
    }
}

将该处理器绑定到SAML认证过滤器中:

@Configuration
public class SamlSecurityConfig extends WebSecurityConfigurerAdapter {

    private final CustomSAMLAuthenticationFailureHandler customFailureHandler;
    private final SAMLAuthenticationProvider samlAuthProvider;
    private final SAMLUserDetailsService samlUserDetailsService;

    public SamlSecurityConfig(CustomSAMLAuthenticationFailureHandler customFailureHandler,
                              SAMLAuthenticationProvider samlAuthProvider,
                              SAMLUserDetailsService samlUserDetailsService) {
        this.customFailureHandler = customFailureHandler;
        this.samlAuthProvider = samlAuthProvider;
        this.samlUserDetailsService = samlUserDetailsService;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
            .anyRequest().authenticated()
            .and()
            .apply(saml())
            .authenticationProvider(samlAuthProvider)
            .userDetailsService(samlUserDetailsService)
            .processingFilter(samlProcessingFilter());
    }

    private SAMLProcessingFilter samlProcessingFilter() throws Exception {
        SAMLProcessingFilter filter = new SAMLProcessingFilter();
        filter.setAuthenticationManager(authenticationManager());
        filter.setAuthenticationFailureHandler(customFailureHandler);
        return filter;
    }
}

方案2:配置Spring Security全局异常处理

通过Security配置统一处理认证类异常:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private final ObjectMapper objectMapper;

    public SecurityConfig(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
            .anyRequest().authenticated()
            .and()
            .exceptionHandling()
            .authenticationEntryPoint((request, response, authException) -> {
                Throwable cause = authException.getCause();
                HttpStatus status = HttpStatus.UNAUTHORIZED;
                String message = "认证失败";

                if (cause instanceof IllegalArgumentException) {
                    status = HttpStatus.BAD_REQUEST;
                    message = cause.getMessage();
                }

                response.setStatus(status.value());
                response.setContentType(MediaType.APPLICATION_JSON_VALUE);
                ApiException apiException = new ApiException(message, status, ZonedDateTime.now(ZoneId.of("Z")));
                objectMapper.writeValue(response.getWriter(), apiException);
            });
    }
}
关键说明

Spring Security过滤器链的执行时机早于Spring MVC控制器,因此@ControllerAdvice无法拦截该阶段的异常,必须通过Security自身的异常处理机制处理。若loadUserBySAML抛出的异常被包装为AuthenticationException,需通过exception.getCause()获取原始异常类型,再匹配对应的响应逻辑。

内容的提问来源于stack exchange,提问作者Leon Gal

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 06:20:14