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
相关产品推荐
相关产品推荐

