Spring Security 5禁用默认安全机制及自定义认证失败返回401问题
我之前从Spring Boot 1.5迁移到Spring Security 5时也踩过这个坑,ExceptionTranslationFilter确实会在你没处理好响应的情况下“抢戏”,要么抛异常要么覆盖状态码。咱们一步步来搞定它:
1. 完善自定义认证失败处理器MyAuthHandler
核心是要确保响应被完全提交,不给后续过滤器留修改的机会。别调用父类的默认方法,直接自己处理状态码并刷新缓冲区:
public class MyAuthHandler extends SimpleUrlAuthenticationFailureHandler { @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { // 直接设置401状态码 response.setStatus(HttpStatus.UNAUTHORIZED.value()); // 强制刷新缓冲区,确保响应已经发送给客户端 response.flushBuffer(); // 不要调用super.onAuthenticationFailure(),避免触发默认的错误跳转或sendError逻辑 } }
2. 配置ExceptionTranslationFilter的行为
ExceptionTranslationFilter的职责是处理未捕获的AuthenticationException和AccessDeniedException,咱们需要自定义它的入口点,确保即使有未被过滤器处理的认证失败场景,也返回401:
在你的configure(HttpSecurity httpSec)方法中添加:
httpSec.exceptionHandling() // 处理未认证的请求(比如直接访问需要认证的接口但没走自定义过滤器) .authenticationEntryPoint((request, response, authException) -> { response.setStatus(HttpStatus.UNAUTHORIZED.value()); response.flushBuffer(); }) // 如果你需要处理权限不足的情况(比如认证成功但没权限),可以自定义这里,默认返回403 .accessDeniedHandler((request, response, accessDeniedException) -> { response.setStatus(HttpStatus.FORBIDDEN.value()); response.flushBuffer(); });
3. 确认自定义过滤器的执行逻辑
检查你的AbstractPreAuthenticatedProcessingFilter实现,确保在认证失败时,调用完MyAuthHandler后不再抛出异常,也不要继续执行过滤器链。好在AbstractPreAuthenticatedProcessingFilter的默认逻辑就是调用unsuccessfulAuthentication后直接return,不会继续往下走,这个你当前的配置是没问题的,但要确保你没修改这个逻辑。
为什么Spring 4里没问题?
Spring Security 5对ExceptionTranslationFilter的逻辑做了调整,它会更严格地检查响应是否已经提交,如果没提交就会覆盖状态码。所以核心就是要确保响应在认证失败处理器中被完全提交,让后续过滤器无法修改。
这样配置后,不管是自定义过滤器处理的认证失败,还是其他未认证的请求,都会返回干净的401状态码,不会被ExceptionTranslationFilter干扰啦!
内容的提问来源于stack exchange,提问作者Kylar

